Ejemplo n.º 1
1
class PrincePDFDialog(QDialog):
    prince_log = ''

    # GUI definition
    def __init__(self, gui, icon, do_user_config):
        QDialog.__init__(self, gui)
        self.icon = icon
        self.gui = gui
        self.do_user_config = do_user_config

        self.setAttribute(Qt.WA_DeleteOnClose)

        self.setWindowTitle(_('Prince PDF'))
        self.setWindowIcon(icon)

        self.l = QGridLayout()
        self.setLayout(self.l)

        self.image = QLabel()
        self.image.setPixmap(icon.pixmap(120, 120))
        self.l.addWidget(self.image, 1, 1, 4, 1, Qt.AlignVCenter)

        self.convert_to_PDF_button = QPushButton(_('Convert to &PDF'), self)
        self.convert_to_PDF_button.clicked.connect(self.convert_to_PDF)
        self.convert_to_PDF_button.setDefault(True)
        self.convert_to_PDF_button.setToolTip(_('<qt>Start the conversion process</qt>'))
        self.l.addWidget(self.convert_to_PDF_button, 1, 2, Qt.AlignVCenter)

        self.view_log = QPushButton(_('&View log'), self)
        self.view_log.clicked.connect(self.show_log)
        self.view_log.setToolTip(_('<qt>Display the log from the last Prince run</qt>'))
        self.l.addWidget(self.view_log, 2, 2, Qt.AlignVCenter)
        self.view_log.hide()

        self.conf_button = QPushButton(_('Con&figure'), self)
        self.conf_button.clicked.connect(self.config)
        self.conf_button.setToolTip(_('<qt>Configure this plugin</qt>'))
        self.l.addWidget(self.conf_button, 4, 2, Qt.AlignVCenter)

        self.info = QLabel()
        self.l.addWidget(self.info, 5, 1, 1, -1)

        self.suggestion = QLabel()
        self.suggestion.setAlignment(Qt.AlignCenter)
        self.l.addWidget(self.suggestion, 6, 1, 1, -1)

        self.l.setColumnStretch(1, 1)
        self.l.setColumnStretch(2, 10)
        self.l.setRowStretch(1, 1)
        self.l.setRowStretch(2, 1)
        self.l.setRowStretch(3, 10)
        self.l.setRowStretch(4, 1)
        self.l.setRowStretch(5, 1)
        self.l.setRowStretch(6, 1)
        self.l.setRowMinimumHeight(1, 1)
        self.l.setRowMinimumHeight(2, 1)
        self.l.setRowMinimumHeight(3, 1)
        self.l.setRowMinimumHeight(4, 1)
        self.l.setRowMinimumHeight(5, 1)
        self.l.setRowMinimumHeight(6, 1)

        self.buttons = QDialogButtonBox(QDialogButtonBox.Close | QDialogButtonBox.Help)
        self.l.addWidget(self.buttons, 7, 1, 1, -1)
        self.buttons.rejected.connect(self.reject)
        self.buttons.helpRequested.connect(self.about)

        self.adjustSize()

        # Update the info now and every time the selection or the data changes
        self.gui.library_view.model().dataChanged.connect(self.update_info)
        self.gui.library_view.selectionModel().selectionChanged.connect(self.update_info)
        self.update_info()

    def update_info(self):
        '''
        Update the info on top of the window
        '''

        self.db = self.gui.current_db
        # Get selected rows
        rows = self.gui.library_view.selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            self.info.setText(_('<b>No books selected</b>'))
            self.info.setAlignment(Qt.AlignCenter)
            self.suggestion.setText(_('Select one single book'))
            self.selected = None
            self.convert_to_PDF_button.setEnabled(False)
        elif len(rows) > 1:
            self.info.setText(_('<b>Many books selected</b>'))
            self.info.setAlignment(Qt.AlignCenter)
            self.suggestion.setText(_('Select one single book'))
            self.selected = None
            self.convert_to_PDF_button.setEnabled(False)
        else:
            # If there is only one selected book, enable conversion
            # and show list of available formats (formats in prefs are bold)
            book_id = self.gui.library_view.model().id(rows[0])
            fmts = self.db.formats(book_id, index_is_id=True)
            if (fmts):
                fmts = fmts.split(',')
            else: 
                fmts = [_('<i>none</i>')]
            available = False
            for i,fmt in enumerate(fmts):
                fmts[i] = fmt.lower()
                if fmts[i] in prefs['formats']:
                    fmts[i] = '<b>%s</b>' % fmts[i]
                    available = True
            self.info.setText(_('Available formats: %s') % ', '.join(fmts))
            self.info.setAlignment(Qt.AlignLeft)
            # Conversion enabled only if some "preferred" format is found
            if (available):
                self.suggestion.setText(_('Ready'))
                self.selected = book_id
                self.convert_to_PDF_button.setEnabled(True)
            else:
                self.suggestion.setText(_('No preferred format available'))
                self.selected = None
                self.convert_to_PDF_button.setEnabled(False)

    def about(self):
        '''
        Display a short help message
        '''
        from os.path import join
        from calibre.ptempfile import TemporaryDirectory
        from calibre.gui2.dialogs.message_box import MessageBox
        from calibre_plugins.prince_pdf.help import help_txt, license_txt
        from calibre_plugins.prince_pdf import PrincePDFPlugin
        from calibre_plugins.prince_pdf import __license__

        author = PrincePDFPlugin.author
        version = "%i.%i.%i" % PrincePDFPlugin.version
        license = __license__
        with TemporaryDirectory('xxx') as tdir:
          for x in ('prince_icon.png', 'small_icon.png'):
            with open(join(tdir, x),'w') as f:
              f.write(get_resources('images/' + x))
          help_box = MessageBox(type_ = MessageBox.INFO, \
                                title = _('About the Prince PDF Plugin'), \
                                msg = help_txt % {'author':author, 'version':version, 'license':license, 'dir':tdir, 'code':'style="font-family:monospace ; font-weight:bold"'}, \
                                det_msg = 'Copyright \u00a9 %s\n%s' % (__copyright__, license_txt), \
                                q_icon = self.icon, \
                                show_copy_button = False)
          #help_box.gridLayout.addWidget(help_box.icon_widget,0,0,Qt.AlignTop)
          help_box.gridLayout.setAlignment(help_box.icon_widget,Qt.AlignTop)
          help_box.exec_()

    def convert_to_PDF(self):
        '''
        Unpack and convert the currently selected book to PDF
        '''
        from calibre.gui2 import error_dialog
        from calibre.constants import DEBUG

        # Get available formats
        book_id = self.selected
        fmts = self.db.formats(book_id, index_is_id=True)
        fmts = fmts.lower().split(',')
        # Process only the first format matching the 'formats' configuration option
        for fmt in prefs['formats']:
            fmt = fmt.lower()
            if (not fmt in fmts): continue
            mi = self.db.get_metadata(book_id, index_is_id=True, get_cover=False)
            pdf_base_file = self.get_filename(book_id, mi)

            # This is the actual code:
            if DEBUG: print('===========')
            # Unpack the book and call the conversion dialog
            (opf, oeb) = self.unpack(book_id, fmt)
            if (opf == None or oeb == None):
               return error_dialog(self.gui, _('Cannot convert to PDF'), _('Format not supported: %s') % fmt, show=True)
            convert_dialog = ConvertDialog(mi, fmt, opf, oeb, self.icon)
            convert_dialog.pdf_file = pdf_base_file
            pdf_file = ''
            if (convert_dialog.exec_()):
                pdf_file = convert_dialog.pdf_file
            self.prince_log = convert_dialog.prince_log
            # After the dialog returns, pdf_file has the output file path,
            # and prince_log has the Prince console output
            if DEBUG: print(_('PDF file: %s') % pdf_file)
            # If there is any log, enable the View log button
            if (self.prince_log):
                self.view_log.show()
                log_msg = _(' Check the log.')
            else:
                self.view_log.hide()
                log_msg = ''
            # If the conversion failed, pdf_file will be None,
            if (pdf_file == None):
                error_dialog(self.gui, _('Could not convert to PDF'), _('The conversion failed.') + log_msg , show=True)
            # If the user cancelled the dialog, pdf_file will be ''
            if (pdf_file):
                # Set the metadata in the PDF and add it or save it
                try:
                    self.set_pdf_metadata(mi, pdf_file)
                except:
                    error_dialog(self.gui, _('Could not convert to PDF'), _("Error reading or writing the PDF file:\n%s" % pdf_file), show=True)
                    return
                if (prefs['add_book']):
                    self.add_pdf(book_id, pdf_file, ('pdf' in fmts))
                else:
                    self.save_pdf(pdf_file, pdf_base_file)
	    if DEBUG: print('===========')
	    return
        # No matching format in the book
        return error_dialog(self.gui, _('Cannot convert to PDF'), _('No supported format available'), show=True)

    def show_log(self):
        '''
        Display the Prince log dialog
        '''
        msg = LogDialog(self.prince_log, self.icon)
        msg.exec_()

    def config(self):
        '''
        Display the configuration dialog
        '''
        self.do_user_config(parent=self)

    def get_filename(self, book_id, mi):
        '''
        Obtain a filename from the save_to_disk template
        :param book_id: The book identifier
        :param mi: The book metadata
        '''
        from os.path import join
        from calibre.library.save_to_disk import get_components, config
        from calibre import sanitize_file_name_unicode
        from calibre.utils.filenames import ascii_filename

        opts = config().parse()
        components = get_components(opts.template, mi, book_id, opts.timefmt,
                                    sanitize_func=(ascii_filename if opts.asciiize else sanitize_file_name_unicode),
                                    to_lowercase=opts.to_lowercase, replace_whitespace=opts.replace_whitespace)
        base_path = join(*components)
        return '%s.pdf' % base_path

    def unpack(self, book_id, fmt):
        '''
        Unpack the book in a temporary directory
        :param book_id: The book identifier
        :param fmt: The format to unpack
        '''
        from calibre.constants import DEBUG
        from calibre.ptempfile import PersistentTemporaryDirectory
        from calibre.ebooks.tweak import get_tools
        from calibre.ebooks.oeb.base import OEBBook
        from calibre.ebooks.oeb.reader import OEBReader
        from calibre.utils.logging import default_log
        from calibre_plugins.prince_pdf.dummy_preprocessor import dummy_preprocessor

        book_file = self.db.format(book_id, fmt, index_is_id=True, as_path=True)
        if DEBUG: print(_('Unpacking book...'))
        tdir = PersistentTemporaryDirectory('_unpack')
        exploder = get_tools(fmt)[0]
        if (exploder == None): return (None, None)
        opf = exploder(book_file, tdir)
        html_preprocessor = dummy_preprocessor()
        css_preprocessor = dummy_preprocessor()
        oeb = OEBBook(default_log, html_preprocessor, css_preprocessor)
        OEBReader()(oeb, opf)
        return (opf, oeb)

    def set_pdf_metadata(self, mi, pdf_file):
        '''
        Set the metadata in the PDF file
        :param mi: The book metadata
        :param pdf_file: The path to the PDF file
        '''
        from calibre.constants import DEBUG
        from calibre.ebooks.metadata.pdf import set_metadata

        if DEBUG: print(_('Setting metadata...'))
        pdf_stream = open(pdf_file, 'r+b')
        set_metadata(pdf_stream, mi)
        pdf_stream.close()

    def add_pdf(self, book_id, pdf_file, exists):
        '''
        Add the PDF file to the book record, asking for replacement
        :param book_id: The book identifier
        :param pdf_file: The path to the PDF file
        :param exists: True if there is already a PDF in the book
        '''
        from calibre.constants import DEBUG
        from calibre.gui2.dialogs.message_box import MessageBox

        add_it = True
        if (exists):
            msg = MessageBox(MessageBox.QUESTION, _('Existing format'),
                             _('The selected book already contains a PDF format. Are you sure you want to replace it?'),
                             _("The temporary file can be found in:\n%s") % pdf_file)
            msg.toggle_det_msg()
            add_it = (msg.exec_())
        if (add_it):
            if DEBUG: print(_('Adding PDF...'))
            self.db.new_api.add_format(book_id, 'pdf', pdf_file)
            self.gui.library_view.model().refresh_ids([book_id])
            self.gui.library_view.refresh_book_details()
            self.gui.tags_view.recount()

    def save_pdf(self, pdf_file, pdf_base_file):
        '''
        Save the PDF file in the final location
        :param pdf_file: The path to the PDF file
        :param pdf_base_file: The desired file name and relative path
        '''
        from os import rename, makedirs
        from os.path import basename, dirname, join, exists
        from calibre.constants import DEBUG
        from calibre.gui2 import choose_dir
        from calibre.gui2.dialogs.message_box import MessageBox
        from calibre.gui2 import error_dialog

        path = choose_dir(self.gui, 'save to disk dialog', _('Choose destination directory'))
        if not path:
            return
        save_file = join(path, pdf_base_file)
        base_dir = dirname(save_file)
        try:
            makedirs(base_dir)
        except BaseException:
            if not exists(base_dir): raise
        try:
            rename(pdf_file, save_file)
        except:
            error_dialog(self.gui, _('Could not save PDF'), _("Error writing the PDF file:\n%s" % save_file), show=True)
            return
        if DEBUG: print(save_file)
        MessageBox(MessageBox.INFO, _('File saved'), _("PDF file saved in:\n%s") % save_file).exec_()
Ejemplo n.º 2
0
class SyncWidget(QWidget):
    def __init__(self, gui, do_user_config, selected_book_ids,
                 is_sync_selected):
        QWidget.__init__(self, gui)

        api.build_request('/limits')

        self.logger = Logger(
            path.join(gui.current_db.library_path, 'bookfusion_sync.log'))
        self.logger.info(
            'Open sync dialog: selected_book_ids={}; is_sync_selected={}'.
            format(selected_book_ids, is_sync_selected))

        if len(selected_book_ids) == 0:
            is_sync_selected = False

        self.worker_thread = None

        self.do_user_config = do_user_config
        self.db = gui.current_db.new_api

        self.selected_book_ids = selected_book_ids

        self.l = QVBoxLayout()
        self.l.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.l)

        self.radio_layout = QVBoxLayout()
        self.l.addLayout(self.radio_layout)

        self.sync_all_radio = QRadioButton('Sync all books')
        self.sync_all_radio.setChecked(not is_sync_selected)
        self.radio_layout.addWidget(self.sync_all_radio)

        sync_selected_radio_label = 'Sync selected books'
        if len(selected_book_ids) > 0:
            sync_selected_radio_label = 'Sync {} selected {}'.format(
                len(selected_book_ids),
                'book' if len(selected_book_ids) == 1 else 'books')
        self.sync_selected_radio = QRadioButton(sync_selected_radio_label)
        self.sync_selected_radio.toggled.connect(self.toggle_sync_selected)
        self.sync_selected_radio.setChecked(is_sync_selected)
        self.sync_selected_radio.setEnabled(len(selected_book_ids) > 0)
        self.radio_layout.addWidget(self.sync_selected_radio)

        self.reupload_possible = len(selected_book_ids) > 0 and len(
            selected_book_ids) <= 100
        if self.reupload_possible:
            for book_id in selected_book_ids:
                identifiers = self.db.get_proxy_metadata(book_id).identifiers
                if not identifiers.get('bookfusion'):
                    self.reupload_possible = False

        self.reupload_checkbox = QCheckBox('Re-upload book files', self)
        self.reupload_checkbox.setVisible(is_sync_selected
                                          and self.reupload_possible)
        self.radio_layout.addWidget(self.reupload_checkbox)

        self.btn_layout = QHBoxLayout()
        self.l.addLayout(self.btn_layout)

        self.config_btn = QPushButton('Configure')
        self.config_btn.clicked.connect(self.config)
        self.btn_layout.addWidget(self.config_btn)

        self.btn_layout.addStretch()

        self.start_btn = QPushButton('Start')
        self.start_btn.clicked.connect(self.start)
        self.btn_layout.addWidget(self.start_btn)

        self.cancel_btn = QPushButton('Cancel')
        self.cancel_btn.clicked.connect(self.cancel)
        self.cancel_btn.hide()
        self.btn_layout.addWidget(self.cancel_btn)

        self.info = QHBoxLayout()
        self.info.setContentsMargins(0, 0, 0, 0)
        self.l.addLayout(self.info)
        self.msg = QLabel()
        self.info.addWidget(self.msg)
        self.info.addStretch()
        self.log_btn = QLabel('<a href="#">Log</a>')
        self.log_btn.linkActivated.connect(self.toggle_log)
        self.log_btn.hide()
        self.info.addWidget(self.log_btn)

        self.log = QTableWidget(0, 2)
        self.log.setHorizontalHeaderLabels(['Book', 'Message'])
        self.log.horizontalHeader().setStretchLastSection(True)
        self.log.hide()
        self.l.addWidget(self.log)

        self.apply_config()

    def __del__(self):
        if self.worker_thread:
            self.worker_thread.quit()
            self.worker_thread.terminate()

    def config(self):
        self.do_user_config(parent=self)
        self.apply_config()

    def apply_config(self):
        configured = bool(prefs['api_key'])
        self.start_btn.setEnabled(configured)

    def toggle_sync_selected(self, is_sync_selected):
        if hasattr(self, 'reupload_checkbox'):
            self.reupload_checkbox.setVisible(is_sync_selected
                                              and self.reupload_possible)

    def start(self):
        if self.sync_selected_radio.isChecked(
        ) and self.reupload_checkbox.isChecked():
            reply = QMessageBox.question(
                self, 'BookFusion Sync',
                'Re-uploading book files can potentially result in previous highlights or bookmarks no longer working.\n\nPreviously uploaded files will be overwritten. Are you sure you want to re-upload?',
                QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes)
            if reply != QMessageBox.Yes:
                return

        self.worker = None
        self.valid_book_ids = None
        self.book_log_map = {}
        self.book_progress_map = {}

        if self.sync_selected_radio.isChecked():
            book_ids = list(self.selected_book_ids)
        else:
            book_ids = list(self.db.all_book_ids())

        self.logger.info('Start sync: sync_selected={}; book_ids={}'.format(
            self.sync_selected_radio.isChecked(), book_ids))

        self.in_progress = True
        self.total = len(book_ids)
        self.update_progress(None)
        self.start_btn.hide()
        self.cancel_btn.show()
        self.config_btn.setEnabled(False)
        self.sync_all_radio.setEnabled(False)
        self.sync_selected_radio.setEnabled(False)

        self.worker_thread = QThread(self)

        self.worker = CheckWorker(self.db, self.logger, book_ids)
        self.worker.finished.connect(self.finish_check)
        self.worker.finished.connect(self.worker_thread.quit)
        self.worker.progress.connect(self.update_progress)
        self.worker.limitsAvailable.connect(self.apply_limits)
        self.worker.resultsAvailable.connect(self.apply_results)
        self.worker.aborted.connect(self.abort)
        self.worker.moveToThread(self.worker_thread)

        self.worker_thread.started.connect(self.worker.start)
        self.worker_thread.start()

    def apply_limits(self, limits):
        self.logger.info('Limits: {}'.format(limits))
        self.limits = limits

    def apply_results(self, books_count, valid_ids):
        self.logger.info('Check results: books_count={}; valid_ids={}'.format(
            books_count, valid_ids))
        self.valid_book_ids = valid_ids
        self.books_count = books_count

    def finish_check(self):
        if self.valid_book_ids:
            is_filesize_exceeded = len(self.valid_book_ids) < self.books_count
            is_total_books_exceeded = self.limits[
                'total_books'] and self.books_count > self.limits['total_books']

            if is_filesize_exceeded or is_total_books_exceeded:
                if self.limits['message']:
                    msg_box = QMessageBox(self)
                    msg_box.setWindowTitle('BookFusion Sync')
                    msg_box.addButton(QMessageBox.No)
                    msg_box.addButton(QMessageBox.Yes)
                    msg_box.setText(self.limits['message'])
                    msg_box.setDefaultButton(QMessageBox.Yes)
                    reply = msg_box.exec_()
                    if reply == QMessageBox.Yes:
                        self.start_sync()
                    else:
                        self.in_progress = False
                        self.msg.setText('Canceled.')
                        self.finish_sync()
                else:
                    self.start_sync()
            else:
                self.start_sync()
        else:
            if self.in_progress:
                self.in_progress = False
                self.msg.setText('No supported books selected.')
            self.finish_sync()

    def start_sync(self):
        self.log_btn.show()
        self.log.setRowCount(0)
        self.log.show()

        self.worker_thread = QThread(self)

        book_ids = self.valid_book_ids
        if self.limits['total_books']:
            book_ids = book_ids[:self.limits['total_books']]

        self.total = len(book_ids)

        self.worker = UploadManager(
            self.db, self.logger, book_ids,
            self.sync_selected_radio.isChecked()
            and self.reupload_checkbox.isChecked())
        self.worker.finished.connect(self.finish_sync)
        self.worker.finished.connect(self.worker_thread.quit)
        self.worker.progress.connect(self.update_progress)
        self.worker.uploadProgress.connect(self.update_upload_progress)
        self.worker.started.connect(self.log_start)
        self.worker.skipped.connect(self.log_skip)
        self.worker.failed.connect(self.log_fail)
        self.worker.uploaded.connect(self.log_upload)
        self.worker.updated.connect(self.log_update)
        self.worker.aborted.connect(self.abort)
        self.worker.moveToThread(self.worker_thread)

        self.worker_thread.started.connect(self.worker.start)
        self.worker_thread.start()

    def finish_sync(self):
        if self.in_progress:
            self.msg.setText('Done.')
        self.cancel_btn.hide()
        self.cancel_btn.setEnabled(True)
        self.start_btn.show()
        self.config_btn.setEnabled(True)
        self.sync_all_radio.setEnabled(True)
        self.sync_selected_radio.setEnabled(len(self.selected_book_ids) > 0)

    def abort(self, error):
        self.in_progress = False
        self.msg.setText(error)

    def cancel(self):
        self.in_progress = False
        self.msg.setText('Canceled.')
        self.cancel_btn.setEnabled(False)
        self.worker.cancel()

    def update_progress(self, progress):
        if self.in_progress:
            if isinstance(self.worker, UploadManager):
                msg = 'Synchronizing...'
            else:
                msg = 'Preparing...'
            if progress:
                msg += ' {} of {}'.format(progress + 1, self.total)
            self.msg.setText(msg)

    def update_upload_progress(self, book_id, sent, total):
        if not book_id in self.book_progress_map:
            return

        progress = self.book_progress_map[book_id]

        if sent < total:
            progress.setValue(sent)
            progress.setMaximum(total)
        else:
            progress.setMaximum(0)

    def log_start(self, book_id):
        self.update_log(book_id, None)

    def log_fail(self, book_id, msg):
        self.update_log(book_id, msg)

    def log_skip(self, book_id):
        self.update_log(book_id, 'skipped')

    def log_upload(self, book_id):
        self.update_log(book_id, 'uploaded')

    def log_update(self, book_id):
        self.update_log(book_id, 'updated')

    def toggle_log(self, _):
        self.log.setVisible(not self.log.isVisible())

    def update_log(self, book_id, msg):
        if book_id in self.book_log_map:
            index = self.book_log_map[book_id]
        else:
            index = self.log.rowCount()
            self.book_log_map[book_id] = index

            self.log.insertRow(index)

            title = self.db.get_proxy_metadata(book_id).title
            title_item = QTableWidgetItem(title)
            title_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled
                                | Qt.ItemNeverHasChildren)
            self.log.setItem(index, 0, title_item)

            progress = QProgressBar()
            progress.setMaximum(0)
            self.log.setCellWidget(index, 1, progress)
            self.book_progress_map[book_id] = progress

        if not msg is None:
            del (self.book_progress_map[book_id])
            self.log.setCellWidget(index, 1, None)

            msg_item = QTableWidgetItem(msg)
            msg_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled
                              | Qt.ItemNeverHasChildren)
            self.log.setItem(index, 1, msg_item)

    def maybe_cancel(self):
        if self.worker_thread and self.worker_thread.isRunning():
            reply = QMessageBox.question(
                self, 'BookFusion Sync',
                'Are you sure you want to cancel the currently running process?',
                QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes)
            if reply == QMessageBox.Yes:
                self.cancel()
            else:
                return False
        return True
class AnimationToolsPanel(QFrame):
    
    WIDTH = 1100
    HEIGHT = 45
    
    FRAME_WIDTH = 1
    FRAME_MARGIN = 2
    
    ICON_SIZE = 35
    ICON_BUTTON_WIDTH = 60
    
    IO_BUTTON_WIDTH = 200
    IO_BLOCK_START_X = 292
    
    TIME_INPUT_X = 698
    BUTTON_BLOCK_X = 918
    
    def __init__(self, parent, tools):
        super().__init__(parent)
        self.tools = tools
        self.initUI()
        
    def initUI(self):
        self.resize(AnimationToolsPanel.WIDTH, AnimationToolsPanel.HEIGHT + AnimationToolsPanel.FRAME_MARGIN*2)
        self.setFrameStyle(QFrame.StyledPanel)
        self.setLineWidth(AnimationToolsPanel.FRAME_WIDTH)
        self.setFrameRect(QRect(290, 0, 404, 49))
               
        button_stylesheet = """
                                .QPushButton {
                                    font-weight: bold;
                                    font-size: 13px;
                                    background-color:#E0E0E0;
                                }
                                .QPushButton:pressed {
                                    background-color:#CCCCCC;
                                }
                            """
                       
        self.save_animation = QPushButton('Save Animation', self)
        self.save_animation.resize(AnimationToolsPanel.IO_BUTTON_WIDTH, AnimationToolsPanel.HEIGHT)
        self.save_animation.move(AnimationToolsPanel.IO_BLOCK_START_X, AnimationToolsPanel.FRAME_MARGIN)        
        self.save_animation.setStyleSheet(button_stylesheet)
        self.save_animation.clicked.connect(self.saveXML)
       
        self.load_animation = QPushButton('Load Animation', self)
        self.load_animation.resize(AnimationToolsPanel.IO_BUTTON_WIDTH, AnimationToolsPanel.HEIGHT)
        self.load_animation.move(AnimationToolsPanel.IO_BLOCK_START_X + AnimationToolsPanel.IO_BUTTON_WIDTH, AnimationToolsPanel.FRAME_MARGIN)        
        self.load_animation.setStyleSheet(button_stylesheet)
        self.load_animation.clicked.connect(self.fromXML)
        
        self.player = AnimationPlayer(self)
        self.player.move(0, AnimationToolsPanel.FRAME_MARGIN)
        self.player.show()
        
        self.time_input = TimeInputLine(self)
        self.time_input.move(AnimationToolsPanel.TIME_INPUT_X, AnimationToolsPanel.FRAME_WIDTH)
        self.time_input.addAcceptListener(self.onAcceptListener)
        self.time_input.addCancelListener(self.onCancelListener)
        self.time_input.hide()       
        
        self.time_button = QPushButton('', self)
        self.time_button.setIcon(assets.time)
        self.time_button.setIconSize(QSize(AnimationToolsPanel.ICON_SIZE, AnimationToolsPanel.ICON_SIZE))
        self.time_button.resize(AnimationToolsPanel.ICON_BUTTON_WIDTH, AnimationToolsPanel.HEIGHT)
        self.time_button.move(AnimationToolsPanel.BUTTON_BLOCK_X, AnimationToolsPanel.FRAME_MARGIN)        
        self.time_button.setStyleSheet(button_stylesheet)
        self.time_button.clicked.connect(self.timeFrameListener)
        self.time_button.show()        

        self.copy_button = QPushButton('', self)
        self.copy_button.setIcon(assets.copy)
        self.copy_button.setIconSize(QSize(AnimationToolsPanel.ICON_SIZE, AnimationToolsPanel.ICON_SIZE))
        self.copy_button.resize(AnimationToolsPanel.ICON_BUTTON_WIDTH, AnimationToolsPanel.HEIGHT)
        self.copy_button.move(AnimationToolsPanel.BUTTON_BLOCK_X + AnimationToolsPanel.ICON_BUTTON_WIDTH, AnimationToolsPanel.FRAME_MARGIN)        
        self.copy_button.setStyleSheet(button_stylesheet)
        self.copy_button.clicked.connect(self.copyFrameListener)
        self.copy_button.show() 
        
        self.delete_button = QPushButton('', self)
        self.delete_button.setIcon(assets.delete)
        self.delete_button.setIconSize(QSize(AnimationToolsPanel.ICON_SIZE, AnimationToolsPanel.ICON_SIZE))
        self.delete_button.resize(AnimationToolsPanel.ICON_BUTTON_WIDTH, AnimationToolsPanel.HEIGHT)
        self.delete_button.move(AnimationToolsPanel.BUTTON_BLOCK_X + AnimationToolsPanel.ICON_BUTTON_WIDTH*2, AnimationToolsPanel.FRAME_MARGIN)        
        self.delete_button.setStyleSheet(button_stylesheet)
        self.delete_button.clicked.connect(self.deleteListener)
        self.delete_button.show() 
    
    """ Methods to show/hide the input time and frame-control buttons """
    def showButtonBlock(self):
        self.delete_button.show()
        self.time_button.show()
        self.copy_button.show()
        self.time_input.hide()
    def showInputTime(self):
        self.delete_button.hide()
        self.time_button.hide()
        self.copy_button.hide()  
        self.time_input.label.setText(self.tools.framemenu.getActiveFrame()[0].text().split(":")[0] + ":")
        self.time_input.show()
    
    def hideAll(self):
        self.delete_button.hide()
        self.time_button.hide()
        self.copy_button.hide()
        self.time_input.hide()
        self.save_animation.hide()
        self.load_animation.hide()
        self.setFrameShape(QFrame.NoFrame)
    def showAll(self):
        self.showButtonBlock()
        self.save_animation.show()
        self.load_animation.show()
        self.setFrameShape(QFrame.StyledPanel)
    
    """ time change listeners, which are called by the TimeInput component """
    def onCancelListener(self):
        self.showButtonBlock()    
    def onAcceptListener(self):
        time = self.time_input.spinbox.value()
        self.tools.framemenu.changeFrameTime(time)
        self.showButtonBlock()
    
    """ Listeners for delete, copy and time buttons """
    def deleteListener(self):
        self.tools.framemenu.deleteFrame(self)
    def copyFrameListener(self):
        self.tools.framemenu.copyFrame()
    def timeFrameListener(self):    
        active_frame = self.tools.framemenu.getActiveFrame()
        if not active_frame == None:
            self.showInputTime()  
       
    """ listeners for XML binding and parsing """
    def saveXML(self):
        if len(self.tools.framemenu.getAllFrames()) > 1:
            result = QFileDialog.getSaveFileName(self, 'Choose the destination for the animation file!', '.', 'Animation File (*.armo)')
            if result[0] != "":
                XML().toXML(self.tools.framemenu.getAllFrames(), result[0])
        else:
            QMessageBox.information(self, "Stickman Message", "You need at least 2 frames to save the animation!")
        
    def fromXML(self):
        file = QFileDialog.getOpenFileName(self, "Load Animation", ".", "Animation Files (*.armo)")
        if file[0] != "":
            try:
                frames = XML().fromXML(file[0])
                self.tools.framemenu.removeAllFrames()
                for frame in frames:
                    self.tools.framemenu.addNewFrame(frame)
                if len(frames) > 0:
                    getWorld().setWorldFrom(frames[0])
            except:
                QMessageBox.information(self, "Stickman Message", "The animation file is not valid!")
class AnimationPlayer(QFrame):
    
    STOPPED = 0
    PLAYING = 1
    PAUSED = 2 
    
    WIDTH = 260
    
    def __init__(self, parent):
        super().__init__(parent)           
        self.initUI()  
    
    def initUI(self):
        self.resize(AnimationPlayer.WIDTH, AnimationToolsPanel.HEIGHT)
        self.playing = AnimationPlayer.STOPPED
        
        button_stylesheet = """
                                .QPushButton {
                                    font-weight: bold;
                                    font-size: 13px;
                                    background-color:#E0E0E0;
                                }
                                .QPushButton:pressed {
                                    background-color:#CCCCCC;
                                }
                            """
        
        self.play_button = QPushButton('', self)
        self.play_button.setIcon(assets.play)
        self.play_button.setIconSize(QSize(AnimationToolsPanel.ICON_SIZE, AnimationToolsPanel.ICON_SIZE))
        self.play_button.resize(AnimationToolsPanel.ICON_BUTTON_WIDTH, AnimationToolsPanel.HEIGHT)
        self.play_button.move(0, 0)        
        self.play_button.setStyleSheet(button_stylesheet)
        self.play_button.clicked.connect(self.onPlay)
        self.play_button.show()        
        
        self.clock = Clock(self)
        self.clock.move(AnimationToolsPanel.ICON_BUTTON_WIDTH, 0)
        self.clock.task = self.updateAnimation
        self.clock.hide()
        
        self.stop_button = QPushButton('', self)
        self.stop_button.setIcon(assets.stop)
        self.stop_button.setIconSize(QSize(AnimationToolsPanel.ICON_SIZE, AnimationToolsPanel.ICON_SIZE))
        self.stop_button.resize(AnimationToolsPanel.ICON_BUTTON_WIDTH, AnimationToolsPanel.HEIGHT)
        self.stop_button.move(AnimationPlayer.WIDTH - AnimationToolsPanel.ICON_BUTTON_WIDTH, 0)        
        self.stop_button.setStyleSheet(button_stylesheet)
        self.stop_button.clicked.connect(self.onStop)
        self.stop_button.hide() 
        
        self.old_frame = None
        self.current_frame = None  
        self.next_frame = None       
        self.steps = 0          #number of intermediate steps between two frames
        self.step_counter = 0   #number of steps already taken for the interpolation
    
    """ called when the play button is pressed """      
    def onPlay(self):        
        self.disable()
        self.clock.show()
        self.stop_button.show()
        if self.playing == AnimationPlayer.STOPPED: #starting
            self.play_button.setIcon(assets.pause) 
            self.playing = AnimationPlayer.PLAYING                        
            self.play()
        elif self.playing == AnimationPlayer.PAUSED:
            self.play_button.setIcon(assets.pause) 
            self.playing = AnimationPlayer.PLAYING                        
            self.clock.startClock()
        else:                                       #pausing
            self.play_button.setIcon(assets.play) 
            self.playing = AnimationPlayer.PAUSED 
            self.clock.stopClock()        
            
    """ called when the stop button is pressed or the animation is over """                          
    def onStop(self):
        if not self.playing == AnimationPlayer.STOPPED:
            self.enable()
            self.play_button.setIcon(assets.play) 
            self.playing = AnimationPlayer.STOPPED              
            self.clock.stopClock()
            self.clock.reset()
            self.clock.hide()
            self.stop_button.hide()  
    
    """ Methods which enable and disable screen controls while the animation plays """
    def disable(self):
        self.parent().parent().control_panel.setEnabled(False)
        self.parent().parent().canvas.setEnabled(False)
        self.parent().tools.framemenu.hide()
        self.parent().hideAll()
        
    def enable(self):
        self.parent().parent().control_panel.setEnabled(True)
        self.parent().parent().canvas.setEnabled(True)
        self.parent().tools.framemenu.show()
        self.parent().showAll()
    
    """ Entry method for the animation """
    def play(self):
        if self.parent().tools.framemenu.getActiveFrame() == None:
            self.next_frame = self.parent().tools.framemenu.getFirstFrame()
        else:
            self.next_frame = self.parent().tools.framemenu.getActiveFrame()[1]
        if self.next_frame != None:
            self.reloadFrames()
        else:
            self.onStop()
            
    """ this method loads two frames between which it is required to interpolate.
        It loads frames from the FrameList until there are no more pairs of frames """
    def reloadFrames(self):
        self.clock.stopClock()
        
        self.old_frame = self.next_frame        
        self.current_frame = self.old_frame.copy()
        getWorld().setWorldFrom(self.current_frame)        
        self.next_frame = self.parent().tools.framemenu.getNextFrame(self.old_frame)
        
        if self.next_frame != None:            
            self.steps = (self.next_frame.time*1000)/Clock.TIMER_STEP
            self.step_counter = 0
            self.clock.startClock()
        else:
            self.onStop()
    
    """ Methods used to perform the interpolation job as they are """
    def updateAnimation(self):
        if self.step_counter < self.steps:
            self.step_counter = self.step_counter + 1            
            step_ratio = self.step_counter/self.steps            
            # interpolate all stickmen one by one
            for stickman in self.old_frame.stickmen:                
                new_stickman = self.next_frame.getStickman(stickman.name)                
                # need to check the new stickman, because it can be removed in the next frame
                if new_stickman != None:
                    self.interpolatePosition(stickman, new_stickman, step_ratio)
                    self.interpolateJoints(stickman, new_stickman, step_ratio)        
        else:
            self.reloadFrames()    # called when two frames have interpolated enough to swap the last approximation for the real next frame
    
    """ Interpolates between the positions of stickman body """
    def interpolatePosition(self, old_stickman, new_stickman, ratio):
        x_difference = new_stickman.x - old_stickman.x
        y_difference = new_stickman.y - old_stickman.y
        current_x = x_difference*ratio
        current_y = y_difference*ratio
                    
        modified_stickman = self.current_frame.getStickman(old_stickman.name)
        modified_stickman.x = old_stickman.x + current_x
        modified_stickman.y = old_stickman.y + current_y
    
    """ Interpolates between the positions of stickman joints """
    def interpolateJoints(self, old_stickman, new_stickman, ratio):
        i = 0
        while i < len(old_stickman.joints):
            old_joint = old_stickman.joints[i]
            new_joint = new_stickman.joints[i]                       
                        
            if (old_joint.attachment != None and new_joint.attachment != None):
                # degree difference and its alternatives are used to calculate the shortes path in rotation between two points of stickman
                degree_difference = new_joint.angle - old_joint.angle
                degree_difference_alternative = (2*math.pi - old_joint.angle + new_joint.angle)
                if abs(degree_difference) <= abs(degree_difference_alternative):
                    current_degree_change = degree_difference*ratio
                else:
                    current_degree_change = degree_difference_alternative*ratio
                
                modified_stickman = self.current_frame.getStickman(old_stickman.name)
                degree_by = old_joint.angle + current_degree_change - modified_stickman.joints[i].angle                        
                modified_stickman.joints[i].rotateBy(degree_by)                
            i = i + 1  
Ejemplo n.º 5
0
class MyWidget(QMainWindow, Ui_MainWindow):
    def __init__(self):
        # self.master_tags = [Master_Tag('Недавние', [Tag('ya.ru'), Tag('google.com'),
        #                                             Tag('apple.com')]),
        #                     Master_Tag('Учеба',
        #                                [Tag('wikipedia.com'), Tag('wolframalpha.com'),
        #                                 Tag('dnevnik.ru')])]  # потом из БД доставать буду
        # текущие вкладки надо хранить в виде индексов дабы избежать проблем с ссылками на переменные и копирование и удаление
        self.current_master_index = 0  # тоже достаьт из бд
        self.current_tag_index = 0  # ну и это

        self.max_tag_count = 6

        super().__init__()
        self.setupUi(self)
        self.setGeometry(1, 1, 1, 1)
        self.start_browser()

    def start_browser(self):
        self.con = sqlite3.connect('BrowserDB.db', isolation_level=None)
        self.cur = self.con.cursor()
        self.clear_db()
        self.master_tags = self.import_tags()
        # print(self.master_tags[self.current_master_index].tags[self.current_tag_index].url)
        self.make_master_tags()
        self.make_all_tags()
        self.hide_all_tags()
        self.show_current_tags()
        self.site.setText(self.master_tags[self.current_master_index].tags[
            self.current_tag_index].url)
        self.update_web()
        self.site.returnPressed.connect(self.update_web)
        self.update.pressed.connect(self.update_web)
        self.web.urlChanged.connect(self.change_url_line)
        self.undo.pressed.connect(self.undo_func)
        self.forward.pressed.connect(self.forward_func)

    def clear_db(self):
        self.cur.execute("DELETE FROM Tags WHERE is_deleted=1")
        self.cur.execute("DELETE FROM Masters WHERE is_deleted=1")
        self.con.commit()

    def import_tags(self):
        master_tags = []
        data = self.cur.execute(
            "SELECT title, tags, id FROM Masters").fetchall()
        for elem in data:
            tags = []
            # print(elem[1])
            for tag_id in str(elem[1]).split(';'):
                tag_data = self.cur.execute(
                    "SELECT url, id FROM Tags WHERE id=?",
                    (tag_id, )).fetchall()
                if tag_data != []:
                    tags.append(Tag(tag_data[0][0], tag_data[0][1]))
            # print(elem[0])
            master_tags.append(Master_Tag(str(elem[0]), tags, elem[2]))
            # master_tags[-1].index_in_db = elem[2]
        return master_tags

    def make_master_tags(self):
        for i in range(len(
                self.master_tags)):  # создаем все мастер-теги в дизайне
            self.gridLayout.addWidget(self.master_tags[i], 0, i * 2, 1, 2)
            self.master_tags[i].tag_body.pressed.connect(self.change_master)
            self.master_tags[i].del_btn.pressed.connect(self.delete_master)
            self.master_tags[i].tag_body.index = i
            self.master_tags[i].del_btn.index = i

        not_deleted_num = sum(
            [0 if i.is_deleted else 1 for i in self.master_tags])
        if not_deleted_num < self.max_tag_count:
            try:
                self.add_master_btn.hide()
            except Exception:
                pass
            self.add_master_btn = QPushButton('+')
            self.gridLayout.addWidget(self.add_master_btn, 0,
                                      2 * not_deleted_num, 1, 1)
            self.add_master_btn.pressed.connect(self.create_master)

    def create_master(self):
        name, ok_pressed = QInputDialog.getText(self, 'Новая мастер-вкладка',
                                                'Название новой вкладки:')
        if ok_pressed:
            new_id = int(
                self.cur.execute("SELECT id FROM max_master").fetchall()[0]
                [0]) + 1
            new_tag_id = int(
                self.cur.execute("SELECT id FROM max_id").fetchall()[0][0]) + 1

            self.cur.execute("UPDATE max_master SET id=?", (new_id, ))
            self.cur.execute("UPDATE max_id SET id=?", (new_tag_id, ))
            self.con.commit()
            new_tag = Tag('google.com', new_tag_id)
            self.master_tags.append(Master_Tag(name, [new_tag], new_id))
            self.gridLayout.addWidget(new_tag, 1, 0, 1, 2)
            new_tag.hide()
            not_deleted_num = sum(
                [0 if i.is_deleted else 1 for i in self.master_tags])
            # print(self.master_tags[-1])
            self.gridLayout.addWidget(self.master_tags[-1], 0,
                                      2 * (not_deleted_num - 1), 1, 2)
            # self.master_tags[len(self.master_tags) - 1].show()
            self.master_tags[len(self.master_tags) -
                             1].tag_body.pressed.connect(self.change_master)
            self.master_tags[len(self.master_tags) -
                             1].del_btn.pressed.connect(self.delete_master)
            self.master_tags[len(self.master_tags) -
                             1].tag_body.index = len(self.master_tags) - 1
            self.master_tags[len(self.master_tags) -
                             1].del_btn.index = len(self.master_tags) - 1
            # print('hello')
            # print(new_tag_id, new_id)
            # print(self.cur.execute("SELECT * FROM Tags WHERE id=?", (new_tag_id, )).fetchall())
            self.cur.execute("INSERT INTO Tags VALUES (?, 'google.com', 0)",
                             (new_tag_id, ))
            # print('tags ready')
            self.cur.execute("INSERT INTO Masters VALUES (?, ?, 0, ?)",
                             (new_id, name, str(new_tag_id)))

            self.current_master_index = not_deleted_num - 1
            self.current_tag_index = 0
            self.add_master_btn.hide()
            if not_deleted_num < self.max_tag_count:
                self.add_master_btn = QPushButton('+')
                self.add_master_btn.pressed.connect(self.create_master)
                self.gridLayout.addWidget(self.add_master_btn, 0,
                                          (not_deleted_num - 1) * 2, 1, 1)
                self.add_master_btn.show()
            self.con.commit()
            self.hide_all_master()
            self.show_current_master_tags()
            self.hide_all_tags()
            self.show_current_tags()
            self.update_web()

    def show_current_master_tags(self):
        g = 0
        for i in range(len(self.master_tags)):
            if not self.master_tags[i].is_deleted:
                self.master_tags[i].show()
                if g >= 1:
                    self.gridLayout.removeWidget(self.master_tags[i])
                    self.gridLayout.addWidget(self.master_tags[i], 0,
                                              2 * (i - g), 1, 2)
            else:
                g += 1
        not_deleted_num = sum(
            [0 if i.is_deleted else 1 for i in self.master_tags])
        if not_deleted_num < self.max_tag_count:  # создание кнопочки для добавления вкладки в текущий мастер
            try:
                self.add_master_btn.hide()
            except Exception:
                pass
            self.add_master_btn = QPushButton('+')
            self.gridLayout.addWidget(self.add_master_btn, 0,
                                      2 * not_deleted_num, 1, 1)
            self.add_master_btn.pressed.connect(self.create_master)
        else:
            self.add_master_btn.hide()

    def delete_master(self):
        # print(self.master_tags[self.sender().index])
        not_deleted_tag = sum(
            [0 if i.is_deleted else 1 for i in self.master_tags])
        if not_deleted_tag > 1:
            self.master_tags[self.sender().index].is_deleted = True
            master_id = self.master_tags[self.sender().index].id
            tags = str(
                self.cur.execute("SELECT tags FROM Masters WHERE id=?",
                                 (master_id, )).fetchall()[0][0]).split(';')
            for tag_id in tags:
                self.cur.execute("DELETE FROM Tags WHERE id=?", (tag_id, ))
            self.cur.execute("UPDATE Masters SET is_deleted=1 WHERE id=?",
                             (master_id, ))
            self.con.commit()
            self.show_current_master_tags()
            self.current_master_index = 0  # брать из истории
            self.current_tag_index = 0  # тоже брать из истории ОБРАБАТЫВАТЬ ЗАКРЫТЫЕ ВКЛАДКИ
            self.hide_all_master()
            self.show_current_master_tags()
            self.hide_all_tags()
            self.show_current_tags()
            try:
                self.add_master_btn.hide()
            except Exception:
                pass
            self.add_master_btn = QPushButton('+')
            self.add_master_btn.pressed.connect(self.create_master)
            not_deleted_num = sum(
                [0 if i.is_deleted else 1 for i in self.master_tags])
            self.gridLayout.addWidget(self.add_master_btn, 0,
                                      not_deleted_num * 2, 1, 1)
            self.add_master_btn.show()
            self.site.setText(self.master_tags[self.current_master_index].tags[
                self.current_tag_index].url)
            self.update_web()

    def delete_all_current_tags(
            self):  # убираем все тэги из поля зрения пользователя
        for i in range(len(self.master_tags[self.current_master_index].tags)):
            # self.master_tags[self.current_master_index].tags[i].hide()
            self.gridLayout.removeWidget(
                self.master_tags[self.current_master_index].tags[i])
            sip.delete(self.master_tags[self.current_master_index].tags[i])
            self.master_tags[self.current_master_index].tags[i].deleteLater()

    def show_current_tags(self):  # показываем тэги текущего мастер-тэга
        g = 0
        for i in range(len(self.master_tags[self.current_master_index].tags)):
            if not self.master_tags[
                    self.current_master_index].tags[i].is_deleted:
                self.master_tags[self.current_master_index].tags[i].show()
                if g >= 1:
                    self.gridLayout.removeWidget(
                        self.master_tags[self.current_master_index].tags[i])
                    self.gridLayout.addWidget(
                        self.master_tags[self.current_master_index].tags[i], 1,
                        2 * (i - g), 1, 2)
            else:
                g += 1
        deleted_num = sum([
            0 if i.is_deleted else 1
            for i in self.master_tags[self.current_master_index].tags
        ])
        if deleted_num < self.max_tag_count:  # создание кнопочки для добавления вкладки в текущий мастер
            try:
                self.add_btn.hide()
            except Exception:
                pass
            self.add_btn = QPushButton('+')
            self.gridLayout.addWidget(self.add_btn, 1, 2 * deleted_num, 1, 1)
            self.add_btn.pressed.connect(self.create_tag)

    def create_tag(self):
        new_id = int(
            self.cur.execute("SELECT id FROM max_id").fetchall()[0][0]) + 1
        # print(new_id)
        self.cur.execute("UPDATE max_id SET id=?", (new_id, ))
        self.master_tags[self.current_master_index].add_tag(
            Tag('google.com', new_id))
        self.cur.execute("INSERT INTO Tags VALUES (?, 'google.com', 0)",
                         (new_id, ))
        master_id = self.master_tags[self.current_master_index].id
        # print(master_id)
        some_shit = str(
            self.cur.execute("SELECT tags FROM Masters WHERE id=?",
                             (master_id, )).fetchall()[0][0])
        # print(f'before {some_shit}', str(new_id))
        # print(some_shit + ';' + str(new_id))
        some_shit = some_shit + ';' + str(new_id)
        # print(f' after {some_shit}')
        self.cur.execute("UPDATE Masters SET tags=? WHERE id=?",
                         (str(some_shit), master_id))
        self.con.commit()
        self.current_tag_index = len(
            self.master_tags[self.current_master_index].tags) - 1
        not_deleted_num = sum([
            0 if i.is_deleted else 1
            for i in self.master_tags[self.current_master_index].tags
        ])
        self.gridLayout.addWidget(
            self.master_tags[self.current_master_index].tags[-1], 1,
            2 * (not_deleted_num - 1), 1, 2)
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].tag_body.pressed.connect(self.click_tag)
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].del_btn.pressed.connect(self.delete_tag)
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].tag_body.index = self.current_tag_index
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].del_btn.index = self.current_tag_index
        self.master_tags[self.current_master_index].tags[
            self.
            current_tag_index].del_btn.master_index = self.current_master_index
        # print('here')
        self.add_btn.hide()
        self.show_current_tags()
        self.update_web()

    def make_all_tags(self):
        for j in range(len(self.master_tags)):
            for i in range(len(
                    self.master_tags[j].tags)):  # а здесь создаем все тэги
                self.gridLayout.addWidget(self.master_tags[j].tags[i], 1,
                                          i * 2, 1, 2)
                self.master_tags[j].tags[i].tag_body.url = self.master_tags[
                    j].tags[i].url
                self.master_tags[j].tags[i].tag_body.pressed.connect(
                    self.click_tag)
                self.master_tags[j].tags[i].del_btn.pressed.connect(
                    self.delete_tag)
                self.master_tags[j].tags[i].tag_body.index = i
                self.master_tags[j].tags[i].del_btn.index = i
                self.master_tags[j].tags[i].del_btn.master_index = j
                # self.master_tags[self.current_master_index].tags[i].is_deleted = False

    def hide_all_tags(self):
        for j in range(len(self.master_tags)):
            for i in range(len(
                    self.master_tags[j].tags)):  # а здесь прячем все тэги
                self.master_tags[j].tags[i].hide()

    def hide_all_master(self):
        for i in range(len(self.master_tags)):
            self.master_tags[i].hide()

    def make_tags(self):  # создаем все тэги текущнго мастер-тэга
        self.master_tags[self.current_master_index].tags = [
            i for i in self.master_tags[self.current_master_index].tags
            if i is not None
        ]
        # print(self.master_tags[self.current_master_index].tags, self.current_master_index)
        for i in range(len(self.master_tags[
                self.current_master_index].tags)):  # а здесь создаем все тэги
            self.gridLayout.addWidget(
                self.master_tags[self.current_master_index].tags[i], 1, i * 2,
                1, 2)
            self.master_tags[self.current_master_index].tags[i].tag_body.url = \
                self.master_tags[self.current_master_index].tags[i].url
            self.master_tags[
                self.current_master_index].tags[i].tag_body.pressed.connect(
                    self.click_tag)
            self.master_tags[
                self.current_master_index].tags[i].del_btn.pressed.connect(
                    self.delete_tag)
            self.master_tags[
                self.current_master_index].tags[i].tag_body.index = i
            self.master_tags[
                self.current_master_index].tags[i].del_btn.index = i

    def change_master(self):
        self.current_master_index = self.sender().index
        self.current_tag_index = 0
        self.site.setText(self.master_tags[self.current_master_index].tags[
            self.current_tag_index].url)
        self.hide_all_tags()
        self.show_current_tags()
        self.update_web()

    def click_tag(self):
        new_url = self.sender().url
        self.site.setText(new_url)
        self.current_tag_index = self.sender(
        ).index  # self.master_tags[self.master_tags_index]self.sender()
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].update_site_name()
        self.update_web()

    def delete_tag(self):  # еще все индексы переназначить
        # print(self.sender().master_index)
        deleted_num = sum([
            0 if i.is_deleted else 1
            for i in self.master_tags[self.sender().master_index].tags
        ])
        if deleted_num > 1:
            self.master_tags[self.sender().master_index].tags[
                self.sender().index].is_deleted = True
            tag_id = self.master_tags[self.sender().master_index].tags[
                self.sender().index].id
            self.cur.execute("UPDATE Tags SET is_deleted=True WHERE id=?",
                             (tag_id, ))
            self.con.commit()
            self.current_tag_index = 0  # брать из истории
            self.hide_all_tags()
            self.show_current_tags()
            self.site.setText(self.master_tags[
                self.sender().master_index].tags[self.current_tag_index].url)
            self.update_web()
            # print(self.master_tags[self.sender().master_index].tags[self.sender().index], self.sender().master_index)

    def update_web(self):
        url = self.site.text()
        if 'https://' in url:
            url = url[8:]
        url = url.strip()
        if '' in url.split('.') or ' ' in url:
            url = 'google.com/search?q=' + '+'.join(url.split())
        page = WebEnginePage(self.web)
        self.web.setPage(page)
        self.web.load(QUrl('https://' + url))
        self.site.setText('https://' + url)
        self.web.show()
        # self.master_tags[self.current_master_index].tags[self.current_tag_index].update_site_name()

    def change_url_line(self):
        new_url = self.web.url().toString()
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].url = new_url
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].tag_body.url = new_url
        self.master_tags[self.current_master_index].tags[
            self.current_tag_index].update_site_name()
        tag_id = self.master_tags[self.current_master_index].tags[
            self.current_tag_index].id
        self.cur.execute("UPDATE Tags SET url=? WHERE id=?", (
            new_url,
            tag_id,
        ))
        self.con.commit()
        self.site.setText(new_url)

    def undo_func(self):
        self.web.back()

    def forward_func(self):
        self.web.forward()
Ejemplo n.º 6
0
class StickmanList(QWidget):
    
    MARGIN_TOP = 20
    
    BUTTON_WIDTH = 240
    BUTTON_HEIGHT = 40
    BUTTON_GAP = 5
    MAX_LENGTH = 10
    
    ICON_SIZE = 25
    EVENT_SHIFT_X = 845
    
    def __init__(self, parent):
        super().__init__(parent)
        self.resize(StickmanList.BUTTON_WIDTH, StickmanList.BUTTON_HEIGHT*(StickmanList.MAX_LENGTH + 2) + StickmanList.MARGIN_TOP)
        self.initUI()        
                
    def initUI(self):
        self.buttons = list()
        
        self.button_style_passive = """
                          .QPushButton {
                              font-weight: bold;
                              font-size: 20px;
                              background-color:#D3D3D3;
                              border: 1px solid black;
                          }
                      """
        self.button_style_active = """
                          .QPushButton {
                              font-weight: bold;
                              font-size: 20px;
                              background-color:#B4B4B4;
                              border: 2px solid black;
                          }
                      """
                       
        getWorld().addListener(self.onStickmanListener)
        
        """ These are the buttons to control list of names scrolling. start_index is the index which points to the 
            first entry in the button list which is currently shown at the top of the visual list. """
        scroll_button_style = """
                                 .QPushButton {
                                      font-weight: bold;
                                      font-size: 20px;
                                      background-color:#D3D3D3;
                                  }
                                  .QPushButton:pressed {
                                      background-color:#B4B4B4;
                                  }       
                               """
        
        self.scroll_up_button = QPushButton("", self)
        self.scroll_up_button.setIcon(assets.up)
        self.scroll_up_button.setIconSize(QSize(StickmanList.ICON_SIZE*2, StickmanList.ICON_SIZE))
        self.scroll_up_button.resize(StickmanList.BUTTON_WIDTH/2, StickmanList.BUTTON_HEIGHT)        
        self.scroll_up_button.move(0, StickmanList.BUTTON_HEIGHT*(StickmanList.MAX_LENGTH + 1) + StickmanList.MARGIN_TOP)
        self.scroll_up_button.setStyleSheet(scroll_button_style)
        self.scroll_up_button.clicked.connect(self.scrollListUp)
        self.scroll_up_button.hide()
            
        self.scroll_down_button = QPushButton("", self)
        self.scroll_down_button.setIcon(assets.down)
        self.scroll_down_button.setIconSize(QSize(StickmanList.ICON_SIZE*2, StickmanList.ICON_SIZE))
        self.scroll_down_button.resize(StickmanList.BUTTON_WIDTH/2, StickmanList.BUTTON_HEIGHT)
        self.scroll_down_button.move(StickmanList.BUTTON_WIDTH/2, StickmanList.BUTTON_HEIGHT*(StickmanList.MAX_LENGTH + 1) + StickmanList.MARGIN_TOP)
        self.scroll_down_button.setStyleSheet(scroll_button_style)
        self.scroll_down_button.clicked.connect(self.scrollListDown)
        self.scroll_down_button.hide()
        
        self.start_index = 0    #required to track the first element in the list which is shown
        
    """ fixes button positions on the stickmen list after button addition or removal. 
        activates/deactivates buttons depending on which stickman is active.
        In case there are morre than 10 buttons, shows scrolling buttons. Controls which buttons are hidden and which are shown. """            
    def rearrangeButtons(self):    
        i = 0
        for button in self.buttons:
            if i < self.start_index:
                button.hide()
            elif i > (self.start_index + StickmanList.MAX_LENGTH - 1):
                button.hide()
            else:
                button.show()
                button.move(0, (i-self.start_index)*(StickmanList.BUTTON_HEIGHT + StickmanList.BUTTON_GAP))
                if getWorld().isActive(button.text()):
                    button.setStyleSheet(self.button_style_active)
                else:
                    button.setStyleSheet(self.button_style_passive)
            i = i+1 
            
        if len(self.buttons) > StickmanList.MAX_LENGTH:
            self.scroll_up_button.show()
            self.scroll_down_button.show()
        else:
            self.scroll_up_button.hide()
            self.scroll_down_button.hide()
    
    def scrollListUp(self):
        if self.start_index > 0:
            self.start_index = self.start_index - 1
            self.rearrangeButtons()
                       
    def scrollListDown(self):
        if self.start_index < len(self.buttons) - StickmanList.MAX_LENGTH:
            self.start_index = self.start_index + 1
            self.rearrangeButtons()
                
    """ listener method which create on-screen buttons with stickman names or remove them. It redraws buttons if active state changes. Called from the World class """
    def onStickmanListener(self, name, operation):
        if operation == World.ADD_EVENT:
            button = QPushButton(name, self)
            button.resize(StickmanList.BUTTON_WIDTH, StickmanList.BUTTON_HEIGHT)
            button.clicked.connect(self.onMousePressed)
            button.show()
            self.buttons.insert(0, button)    
            self.start_index = 0    
            self.rearrangeButtons()
        elif operation == World.REMOVE_EVENT:
            for button in self.buttons:
                if button.text() == name:
                    self.buttons.remove(button)
                    button.setParent(None)
                    if self.start_index > 0:
                        self.start_index = self.start_index - 1
                    self.rearrangeButtons()
        elif operation == World.ACTIVE_EVENT:
            self.rearrangeButtons()
    
    """ listener method used to switch active states of stickmen """
    def onMousePressed(self):
        getWorld().setActive(self.sender().text())
    
    """ these listeners are required to pass mouse events to the canvas in case there are any, making this list transparent as a result """
    def mousePressEvent(self, event):
        getWorld().mousePressed(event.x() + StickmanList.EVENT_SHIFT_X, event.y() + StickmanList.MARGIN_TOP)   
        
    def mouseReleaseEvent(self, event):
        getWorld().mouseReleased(event.x() + StickmanList.EVENT_SHIFT_X, event.y() + StickmanList.MARGIN_TOP) 
    
    def mouseMoveEvent(self, event):
        getWorld().mouseMoved(event.x() + StickmanList.EVENT_SHIFT_X, event.y() + StickmanList.MARGIN_TOP) 
Ejemplo n.º 7
0
class FrameList(QWidget):
    
    BUTTON_WIDTH = 240
    BUTTON_HEIGHT = 40
    
    MARGIN_TOP = 20
    BUTTON_GAP = 5
    MAX_LENGTH = 10
    
    ICON_SIZE = 25
    EVENT_SHIFT_X = 845
    
    def __init__(self, parent):
        super().__init__(parent)
        self.resize(FrameList.BUTTON_WIDTH, FrameList.BUTTON_HEIGHT*(FrameList.MAX_LENGTH+2) + FrameList.MARGIN_TOP)
        self.initUI()
                
    def initUI(self):
        self.buttons = FrameMap()
        
        self.button_style_passive = """
                          .QPushButton {
                              font-weight: bold;
                              font-size: 20px;
                              background-color:#D3D3D3;
                              border: 1px solid black;
                          }
                      """
        self.button_style_active = """
                          .QPushButton {
                              font-weight: bold;
                              font-size: 20px;
                              background-color:#B4B4B4;
                              border: 2px solid black;
                          }
                      """
        
        """ These are the buttons to control list of names scrolling. start_index is the index which points to the 
            first entry in the button list which is currently shown at the top of the visual list. """
        scroll_button_style = """
                                 .QPushButton {
                                      font-weight: bold;
                                      font-size: 20px;
                                      background-color:#D3D3D3;
                                  }
                                  .QPushButton:pressed {
                                      background-color:#B4B4B4;
                                  }       
                               """
        
        self.scroll_up_button = QPushButton("", self)
        self.scroll_up_button.setIcon(assets.up)
        self.scroll_up_button.setIconSize(QSize(FrameList.ICON_SIZE*2, FrameList.ICON_SIZE))
        self.scroll_up_button.resize(FrameList.BUTTON_WIDTH/2, FrameList.BUTTON_HEIGHT)        
        self.scroll_up_button.move(0, FrameList.BUTTON_HEIGHT*(FrameList.MAX_LENGTH+1) + FrameList.MARGIN_TOP)
        self.scroll_up_button.setStyleSheet(scroll_button_style)
        self.scroll_up_button.clicked.connect(self.scrollListUp)
        self.scroll_up_button.hide()
            
        self.scroll_down_button = QPushButton("", self)
        self.scroll_down_button.setIcon(assets.down)
        self.scroll_down_button.setIconSize(QSize(FrameList.ICON_SIZE*2, FrameList.ICON_SIZE))
        self.scroll_down_button.resize(FrameList.BUTTON_WIDTH/2, FrameList.BUTTON_HEIGHT)
        self.scroll_down_button.move(FrameList.BUTTON_WIDTH/2, FrameList.BUTTON_HEIGHT*(FrameList.MAX_LENGTH+1) + FrameList.MARGIN_TOP)
        self.scroll_down_button.setStyleSheet(scroll_button_style)
        self.scroll_down_button.clicked.connect(self.scrollListDown)
        self.scroll_down_button.hide()
        
        self.start_index = 0
    
    """ Methods for changing the frames on the FrameList """
    def addNewFrame(self, frame):
        button = QPushButton("", self)
        button.resize(FrameList.BUTTON_WIDTH, FrameList.BUTTON_HEIGHT)
        button.clicked.connect(self.onMousePressed)
        button.show()
        self.buttons[button] = frame
        if len(self.buttons) > FrameList.MAX_LENGTH:
            self.start_index = len(self.buttons) - FrameList.MAX_LENGTH
        self.rearrangeButtons()
    
    def deleteFrame(self, caller):
        if not self.buttons.active == None:
            response = QMessageBox.question(caller, 'Frame Remove Message', "Are you sure you want to delete this frame?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if response == QMessageBox.Yes:
                self.buttons.active.setParent(None)
                del self.buttons[self.buttons.active]
                if self.start_index > 0:
                    self.start_index = self.start_index-1
                self.parent().canvas.hideMessage()
        self.rearrangeButtons()
    
    def removeAllFrames(self):
        self.buttons.active = None
        for button in self.buttons.keys().copy():
            button.setParent(None)
            del self.buttons[button]
        self.start_index = 0
        self.rearrangeButtons()
    
    def copyFrame(self):
        if not self.buttons.active == None:            
            button = QPushButton("", self)
            button.resize(FrameList.BUTTON_WIDTH, FrameList.BUTTON_HEIGHT)
            button.clicked.connect(self.onMousePressed)
            button.show()
            
            index = self.buttons.index(self.buttons.active)  
            copy_frame = getWorld().getFrame()
            copy_frame.time = self.buttons[self.buttons.active].time
            self.buttons.insertAt(index+1, button, copy_frame)
            if len(self.buttons) > FrameList.MAX_LENGTH:
                self.start_index = self.start_index + 1
            self.rearrangeButtons()
    
    def changeFrameTime(self, time):
        if not self.buttons.active == None:
            self.buttons[self.buttons.active].time = time
            self.rearrangeButtons()
    
    """ Convenience methods for frame retrieval used by the animation player and some buttons """            
    def getActiveFrame(self):
        if not self.buttons.active == None:
            return (self.buttons.active, self.buttons[self.buttons.active])
        else:
            return None    
    def getFirstFrame(self):
        first_button = self.buttons.first()
        if not first_button == None:
            return self.buttons[first_button]
        else:
            return None    
    def getNextFrame(self, frame):
        return self.buttons.nextValue(frame)
        
    def getAllFrames(self):
        return self.buttons.frames        
    
    """ fixes button positions on the stickmen list after button addition or removal. 
        activates/deactivates buttons depending on which stickman is active.
        In case there are more than 10 buttons, shows scrolling buttons. Controls which buttons are hidden and which are shown. """            
    def rearrangeButtons(self):    
        i = 0
        for button in self.buttons.keys():
            if i == 0:
                button.setText("Frame " + str(i+1) + " : " + "0.0s")
            else:
                button.setText("Frame " + str(i+1) + " : " + str("%0.1f" % (self.buttons[button].time)) + "s") #makes up for removed frames, restoring the naming order
               
            if i < self.start_index:
                button.hide()
            elif i > (self.start_index + FrameList.MAX_LENGTH - 1):
                button.hide()
            else:
                button.show()
                button.move(0, (i-self.start_index)*(FrameList.BUTTON_HEIGHT + FrameList.BUTTON_GAP))
                if button == self.buttons.active:
                    button.setStyleSheet(self.button_style_active)
                else:
                    button.setStyleSheet(self.button_style_passive)
            i = i+1 
        #part which whows or hides up-down scroll buttons    
        if len(self.buttons) > FrameList.MAX_LENGTH:
            self.scroll_up_button.show()
            self.scroll_down_button.show()
        else:
            self.scroll_up_button.hide()
            self.scroll_down_button.hide()
            
        self.parent().tools.animation_tools.showButtonBlock()
                
        
    def scrollListUp(self):
        if self.start_index > 0:
            self.start_index = self.start_index - 1
            self.rearrangeButtons()
                       
    def scrollListDown(self):
        if self.start_index < len(self.buttons)-FrameList.MAX_LENGTH:
            self.start_index = self.start_index + 1
            self.rearrangeButtons()
    
    """ listener method used to switch between currently present frames """
    def onMousePressed(self):
        #sets the world to a copy of the current contents, preserving frame from future changes
        if self.sender() == self.buttons.active:
            self.buttons.active = None
            getWorld().copyWorld()
            self.parent().canvas.hideMessage()
        #sets the world to the current frame contents          
        else:
            open_frame = self.buttons[self.sender()]
            getWorld().setWorldFrom(open_frame)
            self.buttons.active = self.sender()  
            self.parent().canvas.showMessage()       
        self.rearrangeButtons()
    
    """ these listeners are required to pass mouse events to the canvas in case there are any, making this list transparent as a result """
    def mousePressEvent(self, event):
        getWorld().mousePressed(event.x() + FrameList.EVENT_SHIFT_X, event.y() + FrameList.MARGIN_TOP)   
        
    def mouseReleaseEvent(self, event):
        getWorld().mouseReleased(event.x() + FrameList.EVENT_SHIFT_X, event.y() + FrameList.MARGIN_TOP) 
    
    def mouseMoveEvent(self, event):
        getWorld().mouseMoved(event.x() + FrameList.EVENT_SHIFT_X, event.y() + FrameList.MARGIN_TOP)   
Ejemplo n.º 8
0
class WorldToolsPanel(QFrame):
    
    FRAME_WIDTH = 1
    FRAME_MARGIN = 2
    TOOLS_WIDTH = 600
    TOOLS_HEIGHT = 30
    
    INPUT_WIDTH = 585    
    BUTTON_WIDTH = 150    
    
    STICKMAN_X = 300
    STICKMAN_Y = 100
    STICKMAN_NAME_MAX_LENGTH = 15   
    
    def __init__(self, parent):
        super().__init__(parent)
        self.initUI()
        
    def initUI(self):        
        self.setFrameStyle(QFrame.StyledPanel)
        self.setLineWidth(WorldToolsPanel.FRAME_WIDTH)
        
        self.resize(WorldToolsPanel.TOOLS_WIDTH + WorldToolsPanel.FRAME_MARGIN*2, WorldToolsPanel.TOOLS_HEIGHT + WorldToolsPanel.FRAME_MARGIN*2)
        self.setFrameRect(QRect(0, 0, WorldToolsPanel.TOOLS_WIDTH + WorldToolsPanel.FRAME_MARGIN*2, WorldToolsPanel.TOOLS_HEIGHT + WorldToolsPanel.FRAME_MARGIN*2))        
        
        button_stylesheet = """
                                .QPushButton {
                                    font-weight: bold;
                                    font-size: 13px;
                                    background-color:#E0E0E0;
                                }
                                .QPushButton:pressed {
                                    background-color:#CCCCCC;
                                }
                            """
                        
        self.create_stickman = QPushButton('Create Stickman', self)
        self.create_stickman.resize(WorldToolsPanel.BUTTON_WIDTH, WorldToolsPanel.TOOLS_HEIGHT)
        self.create_stickman.move(WorldToolsPanel.FRAME_WIDTH*2, WorldToolsPanel.FRAME_MARGIN)        
        self.create_stickman.setStyleSheet(button_stylesheet)
        self.create_stickman.clicked.connect(self.showCreateDialog)
        
        self.create_stickman_input = InputLine(self)
        self.create_stickman_input.setLabelText("Enter New StickName: ")
        self.create_stickman_input.addOkListener(self.readCreateName)
        self.create_stickman_input.addCancelListener(self.hideCreateDialog)
        self.create_stickman_input.move(WorldToolsPanel.FRAME_MARGIN, WorldToolsPanel.FRAME_MARGIN)
        self.create_stickman_input.hide()
        
        self.delete_stickman = QPushButton('Delete Stickman', self)
        self.delete_stickman.resize(WorldToolsPanel.BUTTON_WIDTH, WorldToolsPanel.TOOLS_HEIGHT)
        self.delete_stickman.move(WorldToolsPanel.BUTTON_WIDTH + WorldToolsPanel.FRAME_MARGIN, WorldToolsPanel.FRAME_MARGIN)        
        self.delete_stickman.setStyleSheet(button_stylesheet)
        self.delete_stickman.clicked.connect(self.showDeleteDialog)
        
        self.delete_stickman_input = InputLine(self)
        self.delete_stickman_input.setLabelText("Enter Name to Delete: ")
        self.delete_stickman_input.addOkListener(self.readDeleteName)
        self.delete_stickman_input.addCancelListener(self.hideDeleteDialog)
        self.delete_stickman_input.move(WorldToolsPanel.FRAME_MARGIN, WorldToolsPanel.FRAME_MARGIN)
        self.delete_stickman_input.hide()
        
        self.change_background = QPushButton('Set Background', self)
        self.change_background.resize(WorldToolsPanel.BUTTON_WIDTH, WorldToolsPanel.TOOLS_HEIGHT)
        self.change_background.move(WorldToolsPanel.BUTTON_WIDTH*2 + WorldToolsPanel.FRAME_MARGIN, WorldToolsPanel.FRAME_MARGIN)        
        self.change_background.setStyleSheet(button_stylesheet)
        self.change_background.clicked.connect(self.findBackground)
        
        self.remove_background = QPushButton('Clear Background', self)
        self.remove_background.resize(WorldToolsPanel.BUTTON_WIDTH, WorldToolsPanel.TOOLS_HEIGHT)
        self.remove_background.move(WorldToolsPanel.BUTTON_WIDTH*3 + WorldToolsPanel.FRAME_MARGIN, WorldToolsPanel.FRAME_MARGIN)        
        self.remove_background.setStyleSheet(button_stylesheet)       
        self.remove_background.clicked.connect(self.clearBackground)
        
    """ Methods which show and hide input lines and buttons. ALso methods for resizing the panel and its frame """
    def showCreateDialog(self):
        self.resizeForInput()
        self.hideButtons()  
        self.create_stickman_input.setText("")
        self.create_stickman_input.setErrorText("")
        self.create_stickman_input.show() 
    def hideCreateDialog(self):        
        self.resizeForButtons()
        self.create_stickman_input.hide()  
        self.showButtons()
        
    def showDeleteDialog(self):
        self.resizeForInput()
        self.hideButtons()
        self.delete_stickman_input.setText("")
        self.delete_stickman_input.setErrorText("")
        self.delete_stickman_input.show() 
    def hideDeleteDialog(self):
        self.resizeForButtons()
        self.delete_stickman_input.hide()  
        self.showButtons()
        
    def hideButtons(self):
        self.create_stickman.hide()
        self.delete_stickman.hide()
        self.change_background.hide()
        self.remove_background.hide()       
    def showButtons(self):
        self.create_stickman.show()
        self.delete_stickman.show()
        self.change_background.show()
        self.remove_background.show()
    
    def resizeForButtons(self):
        self.resize(WorldToolsPanel.TOOLS_WIDTH + WorldToolsPanel.FRAME_MARGIN*2, WorldToolsPanel.TOOLS_HEIGHT + WorldToolsPanel.FRAME_MARGIN*2)
        self.setFrameRect(QRect(0, 0, WorldToolsPanel.TOOLS_WIDTH + WorldToolsPanel.FRAME_MARGIN*2, WorldToolsPanel.TOOLS_HEIGHT + WorldToolsPanel.FRAME_MARGIN*2))  
    def resizeForInput(self):    
        self.resize(WorldToolsPanel.INPUT_WIDTH + WorldToolsPanel.FRAME_MARGIN*2 + InputLine.ERROR_WIDTH, WorldToolsPanel.TOOLS_HEIGHT + WorldToolsPanel.FRAME_MARGIN*2)
        self.setFrameRect(QRect(0, 0, WorldToolsPanel.INPUT_WIDTH + WorldToolsPanel.FRAME_MARGIN*2, WorldToolsPanel.TOOLS_HEIGHT + WorldToolsPanel.FRAME_MARGIN*2))   
    
    """ listener for the file dialog method to open the file for the background"""
    def findBackground(self):
        file = QFileDialog.getOpenFileName(self, "Open Image", ".", "Image Files (*.png *.jpg)")
        if file[0] != "":
            getWorld().setBackground(file[0])
    def clearBackground(self):
        getWorld().clearBackground("#FFFFFF")
           
    """ Listeners to take actions when ok button was pressed and input text has been read """
    def readCreateName(self):
        if self.create_stickman_input.getText() == "":
            self.create_stickman_input.setErrorText("StickName cannot be empty!")
        elif getWorld().exists(self.create_stickman_input.getText()):
            self.create_stickman_input.setErrorText("This StickName is already taken!")
            self.create_stickman_input.setText("")
        elif len(self.create_stickman_input.getText()) >  WorldToolsPanel.STICKMAN_NAME_MAX_LENGTH:
            self.create_stickman_input.setErrorText("StickName is too long!")
            self.create_stickman_input.setText("")
        else:
            getWorld().createStickman(self.create_stickman_input.getText(), WorldToolsPanel.STICKMAN_X, WorldToolsPanel.STICKMAN_Y)
            self.hideCreateDialog()
                   
    def readDeleteName(self):
        if self.delete_stickman_input.getText() == "":
            self.delete_stickman_input.setErrorText("StickName cannot be empty!")
        elif not getWorld().exists(self.delete_stickman_input.getText()):
            self.delete_stickman_input.setErrorText("This StickName does not exist!")
            self.delete_stickman_input.setText("")
        else:
            getWorld().removeStickman(self.delete_stickman_input.getText())
            self.hideDeleteDialog()
Ejemplo n.º 9
0
class PrincePDFDialog(QDialog):
    prince_log = ''

    # GUI definition
    def __init__(self, gui, icon, do_user_config):
        QDialog.__init__(self, gui)
        self.icon = icon
        self.gui = gui
        self.do_user_config = do_user_config

        self.setAttribute(Qt.WA_DeleteOnClose)

        self.setWindowTitle(_('Prince PDF'))
        self.setWindowIcon(icon)

        self.l = QGridLayout()
        self.setLayout(self.l)

        self.image = QLabel()
        self.image.setPixmap(icon.pixmap(120, 120))
        self.l.addWidget(self.image, 1, 1, 4, 1, Qt.AlignVCenter)

        self.convert_to_PDF_button = QPushButton(_('Convert to &PDF'), self)
        self.convert_to_PDF_button.clicked.connect(self.convert_to_PDF)
        self.convert_to_PDF_button.setDefault(True)
        self.convert_to_PDF_button.setToolTip(
            _('<qt>Start the conversion process</qt>'))
        self.l.addWidget(self.convert_to_PDF_button, 1, 2, Qt.AlignVCenter)

        self.view_log = QPushButton(_('&View log'), self)
        self.view_log.clicked.connect(self.show_log)
        self.view_log.setToolTip(
            _('<qt>Display the log from the last Prince run</qt>'))
        self.l.addWidget(self.view_log, 2, 2, Qt.AlignVCenter)
        self.view_log.hide()

        self.conf_button = QPushButton(_('Con&figure'), self)
        self.conf_button.clicked.connect(self.config)
        self.conf_button.setToolTip(_('<qt>Configure this plugin</qt>'))
        self.l.addWidget(self.conf_button, 4, 2, Qt.AlignVCenter)

        self.info = QLabel()
        self.l.addWidget(self.info, 5, 1, 1, -1)

        self.suggestion = QLabel()
        self.suggestion.setAlignment(Qt.AlignCenter)
        self.l.addWidget(self.suggestion, 6, 1, 1, -1)

        self.l.setColumnStretch(1, 1)
        self.l.setColumnStretch(2, 10)
        self.l.setRowStretch(1, 1)
        self.l.setRowStretch(2, 1)
        self.l.setRowStretch(3, 10)
        self.l.setRowStretch(4, 1)
        self.l.setRowStretch(5, 1)
        self.l.setRowStretch(6, 1)
        self.l.setRowMinimumHeight(1, 1)
        self.l.setRowMinimumHeight(2, 1)
        self.l.setRowMinimumHeight(3, 1)
        self.l.setRowMinimumHeight(4, 1)
        self.l.setRowMinimumHeight(5, 1)
        self.l.setRowMinimumHeight(6, 1)

        self.buttons = QDialogButtonBox(QDialogButtonBox.Close
                                        | QDialogButtonBox.Help)
        self.l.addWidget(self.buttons, 7, 1, 1, -1)
        self.buttons.rejected.connect(self.reject)
        self.buttons.helpRequested.connect(self.about)

        self.adjustSize()

        # Update the info now and every time the selection or the data changes
        self.gui.library_view.model().dataChanged.connect(self.update_info)
        self.gui.library_view.selectionModel().selectionChanged.connect(
            self.update_info)
        self.update_info()

    def update_info(self):
        '''
        Update the info on top of the window
        '''

        self.db = self.gui.current_db
        # Get selected rows
        rows = self.gui.library_view.selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            self.info.setText(_('<b>No books selected</b>'))
            self.info.setAlignment(Qt.AlignCenter)
            self.suggestion.setText(_('Select one single book'))
            self.selected = None
            self.convert_to_PDF_button.setEnabled(False)
        elif len(rows) > 1:
            self.info.setText(_('<b>Many books selected</b>'))
            self.info.setAlignment(Qt.AlignCenter)
            self.suggestion.setText(_('Select one single book'))
            self.selected = None
            self.convert_to_PDF_button.setEnabled(False)
        else:
            # If there is only one selected book, enable conversion
            # and show list of available formats (formats in prefs are bold)
            book_id = self.gui.library_view.model().id(rows[0])
            fmts = self.db.formats(book_id, index_is_id=True)
            if (fmts):
                fmts = fmts.split(',')
            else:
                fmts = [_('<i>none</i>')]
            available = False
            for i, fmt in enumerate(fmts):
                fmts[i] = fmt.lower()
                if fmts[i] in prefs['formats']:
                    fmts[i] = '<b>%s</b>' % fmts[i]
                    available = True
            self.info.setText(_('Available formats: %s') % ', '.join(fmts))
            self.info.setAlignment(Qt.AlignLeft)
            # Conversion enabled only if some "preferred" format is found
            if (available):
                self.suggestion.setText(_('Ready'))
                self.selected = book_id
                self.convert_to_PDF_button.setEnabled(True)
            else:
                self.suggestion.setText(_('No preferred format available'))
                self.selected = None
                self.convert_to_PDF_button.setEnabled(False)

    def about(self):
        '''
        Display a short help message
        '''
        from os.path import join
        from calibre.ptempfile import TemporaryDirectory
        from calibre.gui2.dialogs.message_box import MessageBox
        from calibre_plugins.prince_pdf.help import help_txt, license_txt
        from calibre_plugins.prince_pdf import PrincePDFPlugin
        from calibre_plugins.prince_pdf import __license__

        author = PrincePDFPlugin.author
        version = "%i.%i.%i" % PrincePDFPlugin.version
        license = __license__
        with TemporaryDirectory('xxx') as tdir:
            for x in ('prince_icon.png', 'small_icon.png'):
                with open(join(tdir, x), 'w') as f:
                    f.write(get_resources('images/' + x))
            help_box = MessageBox(type_ = MessageBox.INFO, \
                                  title = _('About the Prince PDF Plugin'), \
                                  msg = help_txt % {'author':author, 'version':version, 'license':license, 'dir':tdir, 'code':'style="font-family:monospace ; font-weight:bold"'}, \
                                  det_msg = 'Copyright \u00a9 %s\n%s' % (__copyright__, license_txt), \
                                  q_icon = self.icon, \
                                  show_copy_button = False)
            #help_box.gridLayout.addWidget(help_box.icon_widget,0,0,Qt.AlignTop)
            help_box.gridLayout.setAlignment(help_box.icon_widget, Qt.AlignTop)
            help_box.exec_()

    def convert_to_PDF(self):
        '''
        Unpack and convert the currently selected book to PDF
        '''
        from calibre.gui2 import error_dialog
        from calibre.constants import DEBUG

        # Get available formats
        book_id = self.selected
        fmts = self.db.formats(book_id, index_is_id=True)
        fmts = fmts.lower().split(',')
        # Process only the first format matching the 'formats' configuration option
        for fmt in prefs['formats']:
            fmt = fmt.lower()
            if (not fmt in fmts): continue
            mi = self.db.get_metadata(book_id,
                                      index_is_id=True,
                                      get_cover=False)
            pdf_base_file = self.get_filename(book_id, mi)

            # This is the actual code:
            if DEBUG: print('===========')
            # Unpack the book and call the conversion dialog
            (opf, oeb) = self.unpack(book_id, fmt)
            if (opf == None or oeb == None):
                return error_dialog(self.gui,
                                    _('Cannot convert to PDF'),
                                    _('Format not supported: %s') % fmt,
                                    show=True)
            convert_dialog = ConvertDialog(mi, fmt, opf, oeb, self.icon)
            convert_dialog.pdf_file = pdf_base_file
            pdf_file = ''
            if (convert_dialog.exec_()):
                pdf_file = convert_dialog.pdf_file
            self.prince_log = convert_dialog.prince_log
            # After the dialog returns, pdf_file has the output file path,
            # and prince_log has the Prince console output
            if DEBUG: print(_('PDF file: %s') % pdf_file)
            # If there is any log, enable the View log button
            if (self.prince_log):
                self.view_log.show()
                log_msg = _(' Check the log.')
            else:
                self.view_log.hide()
                log_msg = ''
            # If the conversion failed, pdf_file will be None,
            if (pdf_file == None):
                error_dialog(self.gui,
                             _('Could not convert to PDF'),
                             _('The conversion failed.') + log_msg,
                             show=True)
            # If the user cancelled the dialog, pdf_file will be ''
            if (pdf_file):
                # Set the metadata in the PDF and add it or save it
                try:
                    self.set_pdf_metadata(mi, pdf_file)
                except:
                    error_dialog(
                        self.gui,
                        _('Could not convert to PDF'),
                        _("Error reading or writing the PDF file:\n%s" %
                          pdf_file),
                        show=True)
                    return
                if (prefs['add_book']):
                    self.add_pdf(book_id, pdf_file, ('pdf' in fmts))
                else:
                    self.save_pdf(pdf_file, pdf_base_file)
            if DEBUG: print('===========')
            return
        # No matching format in the book
        return error_dialog(self.gui,
                            _('Cannot convert to PDF'),
                            _('No supported format available'),
                            show=True)

    def show_log(self):
        '''
        Display the Prince log dialog
        '''
        msg = LogDialog(self.prince_log, self.icon)
        msg.exec_()

    def config(self):
        '''
        Display the configuration dialog
        '''
        self.do_user_config(parent=self)

    def get_filename(self, book_id, mi):
        '''
        Obtain a filename from the save_to_disk template
        :param book_id: The book identifier
        :param mi: The book metadata
        '''
        from os.path import join
        from calibre.library.save_to_disk import get_components, config
        from calibre import sanitize_file_name_unicode
        from calibre.utils.filenames import ascii_filename

        opts = config().parse()
        components = get_components(
            opts.template,
            mi,
            book_id,
            opts.timefmt,
            sanitize_func=(ascii_filename
                           if opts.asciiize else sanitize_file_name_unicode),
            to_lowercase=opts.to_lowercase,
            replace_whitespace=opts.replace_whitespace)
        base_path = join(*components)
        return '%s.pdf' % base_path

    def unpack(self, book_id, fmt):
        '''
        Unpack the book in a temporary directory
        :param book_id: The book identifier
        :param fmt: The format to unpack
        '''
        from calibre.constants import DEBUG
        from calibre.ptempfile import PersistentTemporaryDirectory
        from calibre.ebooks.tweak import get_tools
        from calibre.ebooks.oeb.base import OEBBook
        from calibre.ebooks.oeb.reader import OEBReader
        from calibre.utils.logging import default_log
        from calibre_plugins.prince_pdf.dummy_preprocessor import dummy_preprocessor

        book_file = self.db.format(book_id,
                                   fmt,
                                   index_is_id=True,
                                   as_path=True)
        if DEBUG: print(_('Unpacking book...'))
        tdir = PersistentTemporaryDirectory('_unpack')
        exploder = get_tools(fmt)[0]
        if (exploder == None): return (None, None)
        opf = exploder(book_file, tdir)
        html_preprocessor = dummy_preprocessor()
        css_preprocessor = dummy_preprocessor()
        oeb = OEBBook(default_log, html_preprocessor, css_preprocessor)
        OEBReader()(oeb, opf)
        return (opf, oeb)

    def set_pdf_metadata(self, mi, pdf_file):
        '''
        Set the metadata in the PDF file
        :param mi: The book metadata
        :param pdf_file: The path to the PDF file
        '''
        from calibre.constants import DEBUG
        from calibre.ebooks.metadata.pdf import set_metadata

        if DEBUG: print(_('Setting metadata...'))
        pdf_stream = open(pdf_file, 'r+b')
        set_metadata(pdf_stream, mi)
        pdf_stream.close()

    def add_pdf(self, book_id, pdf_file, exists):
        '''
        Add the PDF file to the book record, asking for replacement
        :param book_id: The book identifier
        :param pdf_file: The path to the PDF file
        :param exists: True if there is already a PDF in the book
        '''
        from calibre.constants import DEBUG
        from calibre.gui2.dialogs.message_box import MessageBox

        add_it = True
        if (exists):
            msg = MessageBox(
                MessageBox.QUESTION, _('Existing format'),
                _('The selected book already contains a PDF format. Are you sure you want to replace it?'
                  ),
                _("The temporary file can be found in:\n%s") % pdf_file)
            msg.toggle_det_msg()
            add_it = (msg.exec_())
        if (add_it):
            if DEBUG: print(_('Adding PDF...'))
            self.db.new_api.add_format(book_id, 'pdf', pdf_file)
            self.gui.library_view.model().refresh_ids([book_id])
            self.gui.library_view.refresh_book_details()
            self.gui.tags_view.recount()

    def save_pdf(self, pdf_file, pdf_base_file):
        '''
        Save the PDF file in the final location
        :param pdf_file: The path to the PDF file
        :param pdf_base_file: The desired file name and relative path
        '''
        from os import makedirs
        from os.path import basename, dirname, join, exists
        from shutil import move
        from calibre.constants import DEBUG
        from calibre.gui2 import choose_dir
        from calibre.gui2.dialogs.message_box import MessageBox
        from calibre.gui2 import error_dialog

        path = choose_dir(self.gui, 'save to disk dialog',
                          _('Choose destination directory'))
        if not path:
            return
        save_file = join(path, pdf_base_file)
        base_dir = dirname(save_file)
        try:
            makedirs(base_dir)
        except BaseException:
            if not exists(base_dir): raise
        try:
            move(pdf_file, save_file)
        except:
            error_dialog(self.gui,
                         _('Could not save PDF'),
                         _("Error writing the PDF file:\n%s" % save_file),
                         show=True)
            return
        if DEBUG: print(save_file)
        MessageBox(MessageBox.INFO, _('File saved'),
                   _("PDF file saved in:\n%s") % save_file).exec_()
Ejemplo n.º 10
0
class GUI(QtWidgets.QMainWindow):
    def __init__(self):
        '''Asetetaan muuttujille alkuarvoja ohjelman suorittamiseksi'''
        super().__init__()
        self.title = "Lujuusanalysaattori"
        self.left = 200
        self.top = 200
        self.width = 1300
        self.height = 700
        self.palkin_default_pituus = 5
        self.square_size = 10
        self.ikkuna()
        self.button_height = 75
        self.button_width = 150
        self.button_separation = 25
        self.x = 0
        self.y = 0
        self.palkin_leveys = 700
        self.palkin_korkeus = 75
        self.palkin_keskipiste = 650
        self.palkin_paatypiste = 1000
        self.yksikko_arvo = 0
        self.voima = 20
        self.maks_jannitys = "-"
        self.asteikko_teksti = QGraphicsSimpleTextItem()
        '''Lisää QGraphicsScenen ruudukon piirtämistä varten'''
        self.scene = QtWidgets.QGraphicsScene()
        self.scene.setSceneRect(0, -20, self.width - 200, self.height - 100)
        '''Suoritetaan lukuisia metodeja, jolla ohjelma "alustetaan"'''
        self.aloita_simulaatio()
        self.simulaatioikkuna()
        self.simulaatio_nappi()
        self.materiaali_valikko()
        self.uusi_palkki_nappi()
        self.lisaa_tuki_nappi()
        self.lisaa_ulkoinen_voima_nappi()
        self.poista_ulkoinen_voima_nappi()
        self.vaihda_tuki_nappi()

        Ominaisuudet.alkuarvot(self)
        self.lisaa_palkki()
        self.palkin_pituus_valikko()
        self.yksikko_pituus()
        self.asteikko()
        self.lisaa_asteikko_arvo()
        self.asteikko_teksti.hide()
        self.tulos_teksti()
        self.lisaa_seina_tuki()
        self.lisaa_tuki_alhaalta()
        self.ulkoinen_voima_valikko()
        self.ulkoinen_voima_nuoli_alatuki()
        self.ulkoinen_voima_nuoli_seinatuki()
        Ominaisuudet.alkuarvot(self)
        '''Asetetaan tietyille napeille tietty näkyvyys'''
        self.lisaa_tuki.setEnabled(False)
        self.simuloi.setEnabled(False)
        self.show()

    def ikkuna(self):
        '''Tekee ohjelman pääikkunan'''
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setWindowTitle('Lujuusanalysaattori')
        self.horizontal = QtWidgets.QHBoxLayout()
        '''Luo menubarin'''
        self.uusiAction = QAction("Uusi simulaatio", self)
        self.uusiAction.setStatusTip("Luo uusi rakenne")
        self.uusiAction.triggered.connect(self.uusi_rakenne)
        self.uusiAction.setEnabled(True)
        self.uusiAction.setShortcut("Ctrl+N")

        self.tallennaAction = QAction("Tallenna simulaatio", self)
        self.tallennaAction.setStatusTip("Tallenna simulaatio")
        self.tallennaAction.triggered.connect(self.tallenna_rakenne)
        self.tallennaAction.setEnabled(False)
        self.tallennaAction.setShortcut("Ctrl+S")

        self.avaaAction = QAction("Lataa simulaatio", self)
        self.avaaAction.setStatusTip("Lataa simulaatio tiedostosta")
        self.avaaAction.triggered.connect(self.lataa_tallennettu_rakenne)
        self.avaaAction.setShortcut("Ctrl+O")

        self.exitAction = QAction("Exit", self)
        self.exitAction.setToolTip("Lopeta ohjelma")
        self.exitAction.triggered.connect(self.close_application)
        self.exitAction.setShortcut("Ctrl+E")
        self.statusBar()

        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('&File')
        aboutMenu = mainMenu.addMenu('&About')
        fileMenu.addAction(self.uusiAction)
        fileMenu.addAction(self.avaaAction)
        fileMenu.addAction(self.tallennaAction)
        fileMenu.addAction(self.exitAction)

    def tallenna_rakenne(self):
        '''Hoitaa rakenteen tallentamisen'''
        tallennus = Tallennin.tallenin(self)
        if tallennus == True:
            '''Kerrotaan käyttäjälle, että tallennus onnistui'''
            msgBox = QMessageBox()
            msgBox.setText("Tallennus onnistui!")
            msgBox.setWindowTitle("Onnistunut Tallennus")
            msgBox.setMinimumWidth(50)
            msgBox.addButton(QPushButton('OK'), QMessageBox.NoRole)
            msgBox.exec_()

    def lataa_tallennettu_rakenne(self):
        '''Metodi avaa QFileDialog ikkunan, josta käyttäjä valitsee tiedoston, jossa aiemmin tallennettu rakenne sijaitsee. Vain .txt -tiedostot 
        ovat ladattavissa '''
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        tiedosto, _ = QFileDialog.getOpenFileName(self,
                                                  "Valitse tiedosto",
                                                  "",
                                                  "txt Files (*.txt)",
                                                  options=options)
        lataus = Lataaja.lataaja(self, tiedosto)
        if lataus == False:
            return

        if lataus == True:
            self.uusi_rakenne()
            Lataaja.lataaja(self, tiedosto)
            tuen_tyyppi = Ominaisuudet.palauta_tuen_tyyppi(self)
            '''Jos tuki on seinästä, piirretään sitä vastaava grafiikka'''
            if tuen_tyyppi == 0:
                self.nayta_seina_tuki()
                self.gradient_seina_tuki()
            '''Jos tuki on alhaalta, piirretään sitä vastaava grafiikka'''
            if tuen_tyyppi == 1:
                self.nayta_tuki_alhaalta()
                self.gradient_alatuki()

            if tuen_tyyppi != 2:
                self.vaihda_tuki.show()
                self.lisaa_tuki.hide()
            '''Jos ulkoinen voima on asetettu, piirretään se'''
            ulkoinen_voima = int(
                Ominaisuudet.onko_ulkoinen_voima_asetettu(self))
            if ulkoinen_voima == 1:
                self.nayta_ulkoinen_voima()

            self.nayta_palkki()
            Laskin.laskin(self)
            self.paivita_tulos_teksti()
            self.tulos.show()
            self.sp.setValue(float(Ominaisuudet.palauta_palkin_pituus(self)))
            self.uusiAction.setEnabled(True)
            self.simuloi.setEnabled(True)
            '''Kerrotaan käyttäjälle, että kaikki onnistui'''
            msgBox = QMessageBox()
            msgBox.setText("Lataus onnistui!")
            msgBox.setWindowTitle("Onnistunut lataus")
            msgBox.addButton(QPushButton('OK'), QMessageBox.NoRole)
            msgBox.exec_()

    def aloita_simulaatio(self):
        '''Aloittaa simulaation'''
        self.setCentralWidget(QtWidgets.QWidget())
        self.horizontal = QtWidgets.QHBoxLayout()
        self.centralWidget().setLayout(self.horizontal)

    def simulaatioikkuna(self):
        '''lisää view näyttämistä varten'''
        self.view = QtWidgets.QGraphicsView(self.scene, self)
        self.view.adjustSize()
        self.view.show()
        self.horizontal.addWidget(self.view)

    def uusi_palkki_nappi(self):
        '''Luo Uusi palkki -napin'''
        self.uusi_palkki = QPushButton('Uusi palkki')
        self.uusi_palkki.setToolTip("Lisää uusi palkki")
        self.uusi_palkki.move(0, 0)
        self.uusi_palkki.resize(self.button_width, self.button_height)
        self.uusi_palkki.font = QtGui.QFont()
        self.uusi_palkki.font.setPointSize(12)
        self.uusi_palkki.setFont(self.uusi_palkki.font)
        self.uusi_palkki.setEnabled(True)
        self.scene.addWidget(self.uusi_palkki)
        self.uusi_palkki.clicked.connect(self.nayta_palkki)

    def nayta_palkki(self):
        '''Näyttää kaikki palkkiin liittyvät komponentit sekä asettaa uusi palkki -napin toimimattomaksi'''
        self.rect.show()
        self.palkin_pituus.show()
        self.sp.show()
        self.yksikko.show()
        self.asteikko_teksti.show()
        self.line.show()
        self.nuoli_1.show()
        self.nuoli_2.show()
        self.uusi_palkki.setEnabled(False)
        self.lisaa_tuki.setEnabled(True)
        self.materiaali_valinta.setEnabled(True)

    def lisaa_palkki(self):
        '''lisää palkin'''
        self.rect = QGraphicsRectItem(300, 200, self.palkin_leveys,
                                      self.palkin_korkeus)
        self.rect.setBrush(QBrush(4))
        self.scene.addItem(self.rect)
        self.rect.hide()
        self.lisaa_tuki.setEnabled(True)
        '''Aina kun on uusi palkki luotu, voidaan aloittaa simulaatio alusta'''
        self.uusiAction.setEnabled(True)

    def lisaa_tuki_nappi(self):
        '''Luo Lisää tuki -napin'''
        self.lisaa_tuki = QPushButton("Lisää tuki")
        self.lisaa_tuki.setToolTip("Lisää tuki")
        self.lisaa_tuki.move(0, self.button_height + self.button_separation)
        self.lisaa_tuki.resize(self.button_width, self.button_height)
        self.lisaa_tuki.font = QtGui.QFont()
        self.lisaa_tuki.font.setPointSize(12)
        self.lisaa_tuki.setFont(self.lisaa_tuki.font)
        self.lisaa_tuki.setEnabled(False)
        self.lisaa_tuki.clicked.connect(self.valitse_tuki)
        self.scene.addWidget(self.lisaa_tuki)

    def vaihda_tuki_nappi(self):
        '''Luo vaihda tuki -napin'''
        self.vaihda_tuki = QPushButton("Vaihda tuki")
        self.vaihda_tuki.setToolTip("Vaihda tuki")
        self.vaihda_tuki.move(0, self.button_height + self.button_separation)
        self.vaihda_tuki.resize(self.button_width, self.button_height)
        self.vaihda_tuki.setFont(self.lisaa_tuki.font)
        self.vaihda_tuki.clicked.connect(self.valitse_tuki)
        self.scene.addWidget(self.vaihda_tuki)
        self.vaihda_tuki.hide()

    def valitse_tuki(self):
        '''Tuen valinta.
        Jos tuki on seinästä (tyyppi = 0), kysytään halutaanko vaihtaa.
        Jos haluaa muutetaan tuen grafiikka ja arvo'''

        if Ominaisuudet.palauta_tuen_tyyppi(self) == 0:
            msgBox = QMessageBox()
            msgBox.setText("Haluatko vaihtaa tuen tyyppiä?")
            msgBox.addButton(QPushButton('En'), QMessageBox.NoRole)
            msgBox.addButton(QPushButton('Kyllä'), QMessageBox.YesRole)
            vastaus = msgBox.exec_()
            self.rect.setBrush(QBrush(4))

            if vastaus == 1:
                self.viiva_1.hide()
                self.viiva_2.hide()
                self.viiva_3.hide()
                self.viiva_4.hide()
                self.nayta_tuki_alhaalta()

                if int(Ominaisuudet.onko_ulkoinen_voima_asetettu(self)) == 1:
                    self.viiva.hide()
                    self.nuoli_3.hide()
                    self.viiva_5.show()
                    self.nuoli_6.show()

                Ominaisuudet.tuki(self, 1)
            return
        '''Jos tuki on alhaalta (tyyppi = 1), kysytään halutaanko vaihtaa.
        Jos haluaa muutetaan tuen grafiikka ja arvo'''
        if Ominaisuudet.palauta_tuen_tyyppi(self) == 1:
            msgBox = QMessageBox()
            msgBox.setText("Haluatko vaihtaa tuen tyyppiä?")
            msgBox.addButton(QPushButton('Kyllä'), QMessageBox.YesRole)
            msgBox.addButton(QPushButton('En'), QMessageBox.NoRole)
            vastaus = msgBox.exec_()
            self.rect.setBrush(QBrush(4))

            if vastaus == 0:
                Ominaisuudet.tuki(self, 0)
                self.nuoli_4.hide()
                self.nuoli_5.hide()
                self.nayta_seina_tuki()

                if int(Ominaisuudet.onko_ulkoinen_voima_asetettu(self)) == 1:
                    self.viiva.show()
                    self.nuoli_3.show()
                    self.viiva_5.hide()
                    self.nuoli_6.hide()

            if vastaus == 1:
                pass
        '''Jos tukea ei ole (tyyppi = 2). Tuen tyypin valinta'''
        if Ominaisuudet.palauta_tuen_tyyppi(self) == 2:
            msgBox = QMessageBox()
            msgBox.setText("Valitse tuen tyyppi")
            msgBox.addButton(QPushButton('Seinätuki'), QMessageBox.YesRole)
            msgBox.addButton(QPushButton('Tuki alhaalta'), QMessageBox.NoRole)
            vastaus = msgBox.exec_()
            self.vaihda_tuki.show()
            self.lisaa_tuki.hide()

            if vastaus == 0:
                self.nayta_seina_tuki()
                Ominaisuudet.tuki(self, 0)

            if vastaus == 1:
                self.nayta_tuki_alhaalta()
                Ominaisuudet.tuki(self, 1)
        '''Joka tapauksessa asetetaan ulkoisen voiman lisääminen mahdolliseksi
        sekä maalataan palkki normaaliksi'''
        self.lisaa_ulkoinen_voima.setEnabled(True)
        self.simuloi.setEnabled(True)

    def nayta_seina_tuki(self):
        '''Näytetään seinätukea kuvaavat grafiikat'''
        self.viiva_1.show()
        self.viiva_2.show()
        self.viiva_3.show()
        self.viiva_4.show()

    def nayta_tuki_alhaalta(self):
        '''Näytetään alatukea kuvaavat grafiikat'''
        self.nuoli_4.show()
        self.nuoli_5.show()

    def paivita_tuen_tyyppi(self, tyyppi):
        '''Päivittää tuen tyypin arvon Ominaisuudet luokassa'''
        Ominaisuudet.tuki(self, tyyppi)

    def lisaa_seina_tuki(self):
        '''Piirtää seinätukea kuvaavat viivat sekä asettaa self.tuen_tyyppi arvoksi 
        Asettaa SIMULOI-napin painettavaksi'''
        viiva = QtGui.QPen(QtCore.Qt.black, 2)
        viiva.setStyle(QtCore.Qt.SolidLine)
        self.viiva_1 = QGraphicsLineItem(QtCore.QLineF(300, 202, 275, 225))
        self.viiva_2 = QGraphicsLineItem(QtCore.QLineF(300, 222, 275, 245))
        self.viiva_3 = QGraphicsLineItem(QtCore.QLineF(300, 242, 275, 265))
        self.viiva_4 = QGraphicsLineItem(QtCore.QLineF(300, 262, 275, 285))

        self.scene.addItem(self.viiva_1)
        self.scene.addItem(self.viiva_2)
        self.scene.addItem(self.viiva_3)
        self.scene.addItem(self.viiva_4)
        self.viiva_1.hide()
        self.viiva_2.hide()
        self.viiva_3.hide()
        self.viiva_4.hide()
        tyyppi = 0
        Ominaisuudet.tuki(self, tyyppi)
        self.simuloi.setEnabled(True)

    def lisaa_tuki_alhaalta(self):
        '''Piirtää alhaalta tukemista kuvaavat grafiikat
        sekä asettaa self.tuen_tyyppi arvoksi 1'''
        leveys = 15  #nuolen leveus pikseleissä
        korkeus = 30  #nuuolen korkeus pikseleissä
        '''Nuolen kärkien koordinaatit'''
        nuoli_piste_1 = QtCore.QPointF(305, 275)
        nuoli_piste_2 = QtCore.QPointF(305 - leveys, 275 + korkeus)
        nuoli_piste_3 = QtCore.QPointF(305 + leveys, 275 + korkeus)

        nuoli_piste_4 = QtCore.QPointF(995, 275)
        nuoli_piste_5 = QtCore.QPointF(995 - leveys, 275 + korkeus)
        nuoli_piste_6 = QtCore.QPointF(995 + leveys, 275 + korkeus)
        '''Luodaan nuolia kuvaavat QPolygonF oliot'''
        self.nuoli_4 = QGraphicsPolygonItem(
            QtGui.QPolygonF([nuoli_piste_1, nuoli_piste_2, nuoli_piste_3]))
        self.nuoli_5 = QGraphicsPolygonItem(
            QtGui.QPolygonF([nuoli_piste_4, nuoli_piste_5, nuoli_piste_6]))
        self.nuoli_brush = QtGui.QBrush(1)
        self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2)
        self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine)
        '''Lisätään nuolet sceneen'''
        self.scene.addItem(self.nuoli_4)
        self.scene.addItem(self.nuoli_5)
        self.nuoli_4.hide()
        self.nuoli_5.hide()

        tyyppi = 1
        Ominaisuudet.tuki(self, tyyppi)
        self.simuloi.setEnabled(True)

    def lisaa_ulkoinen_voima_nappi(self):
        '''Luo Lisää ulkoinen voima -napin'''
        self.lisaa_ulkoinen_voima = QPushButton("Lisää ulkoinen voima")
        self.lisaa_ulkoinen_voima.setToolTip("Lisää ulkoinen voima")
        self.lisaa_ulkoinen_voima.move(
            0, 2 * self.button_height + 2 * self.button_separation)
        self.lisaa_ulkoinen_voima.resize(self.button_width, self.button_height)
        self.lisaa_ulkoinen_voima.font = QtGui.QFont()
        self.lisaa_ulkoinen_voima.font.setPointSize(8)
        self.lisaa_ulkoinen_voima.setFont(self.lisaa_ulkoinen_voima.font)
        self.lisaa_ulkoinen_voima.clicked.connect(self.nayta_ulkoinen_voima)
        self.lisaa_ulkoinen_voima.clicked.connect(self.nollaa_gradientti)
        self.lisaa_ulkoinen_voima.setEnabled(False)
        self.scene.addWidget(self.lisaa_ulkoinen_voima)

    def poista_ulkoinen_voima_nappi(self):
        '''Luo poista ulkoinen voima -napin'''
        self.poista_ulkoinen_voima = QPushButton("Poista ulkoinen voima")
        self.poista_ulkoinen_voima.setToolTip("Poista ulkoinen voima")
        self.poista_ulkoinen_voima.move(
            0, 2 * self.button_height + 2 * self.button_separation)
        self.poista_ulkoinen_voima.resize(self.button_width,
                                          self.button_height)
        self.poista_ulkoinen_voima.setFont(self.lisaa_ulkoinen_voima.font)
        self.poista_ulkoinen_voima.clicked.connect(self.piilota_ulkoinen_voima)
        self.poista_ulkoinen_voima.clicked.connect(self.nollaa_gradientti)
        self.scene.addWidget(self.poista_ulkoinen_voima)
        self.poista_ulkoinen_voima.hide()

    def piilota_ulkoinen_voima(self):
        '''Piilotaa kaiken ulkoiseen voimaan liittyvän'''
        self.sp_voima.hide()
        self.yksikko_voima.hide()
        self.ulkoinen_voima.hide()
        self.lisaa_ulkoinen_voima.show()
        self.lisaa_ulkoinen_voima.setEnabled(True)
        self.viiva.hide()
        self.nuoli_3.hide()
        self.viiva_5.hide()
        self.nuoli_6.hide()
        self.poista_ulkoinen_voima.hide()
        self.lisaa_ulkoinen_voima.show()
        self.tulos.hide()

        Ominaisuudet.ulkoinen_voima(self, 0)

    def nayta_ulkoinen_voima(self):
        '''Näytetään ulkoinen voima riippuen tuen tyypistä'''
        self.sp_voima.show()
        self.yksikko_voima.show()
        self.ulkoinen_voima.show()
        self.lisaa_ulkoinen_voima.hide()
        self.poista_ulkoinen_voima.show()

        if int(Ominaisuudet.palauta_tuen_tyyppi(self)) == 0:
            self.viiva.show()
            self.nuoli_3.show()

        if int(Ominaisuudet.palauta_tuen_tyyppi(self)) == 1:
            self.viiva_5.show()
            self.nuoli_6.show()

        Ominaisuudet.ulkoinen_voima(self, 1)

    def ulkoinen_voima_valikko(self):
        '''Luo voiman suuruus -tekstin'''
        self.ulkoinen_voima = QGraphicsSimpleTextItem("Voiman suuruus")
        self.ulkoinen_voima.setPos(600, 5)
        self.ulkoinen_voima.font = QtGui.QFont()
        self.ulkoinen_voima.font.setPointSize(12)
        self.ulkoinen_voima.setFont(self.ulkoinen_voima.font)
        self.lisaa_ulkoinen_voima.setEnabled(False)
        self.scene.addItem(self.ulkoinen_voima)
        self.ulkoinen_voima.hide()
        '''Luo voiman arvon QSpinBoxin'''
        self.sp_voima = QSpinBox()
        self.sp_voima.move(750, 5)
        self.sp_voima.setRange(0, 10000)
        self.sp_voima.setSingleStep(1)
        self.sp_voima.setMinimumHeight(30)
        self.sp_voima.setValue(int(Ominaisuudet.palauta_voima(self)))
        self.sp_voima.valueChanged.connect(self.paivita_voima)
        self.scene.addWidget(self.sp_voima)
        self.sp_voima.hide()
        '''Luo yksikönvalinta QComboBOxin'''
        self.yksikko_voima = QComboBox()
        self.yksikko_voima.addItem("kN", 0)
        self.yksikko_voima.addItem("N", 1)
        self.yksikko_voima.move(820, 5)
        self.yksikko_voima.setMinimumHeight(30)
        self.yksikko_voima.setCurrentIndex(
            int(Ominaisuudet.palauta_voiman_yksikko(self)))
        self.yksikko_voima.setEditable(True)
        self.yksikko_voima.lineEdit().setAlignment(QtCore.Qt.AlignCenter)
        self.scene.addWidget(self.yksikko_voima)
        self.yksikko_voima.currentIndexChanged.connect(
            self.paivita_yksikko_voima)
        self.yksikko_voima.hide()

    def ulkoinen_voima_nuoli_seinatuki(self):
        '''Luo nuolen osoittamaan ulkoisen voiman paikkaa'''
        voima_viiva = QtGui.QPen(QtCore.Qt.black, 2)
        voima_viiva.setStyle(QtCore.Qt.SolidLine)
        '''Nuolen kärkien koordinaatit seinätuelle'''
        nuoli_piste_1 = QtCore.QPointF(self.palkin_paatypiste - 7, 185)
        nuoli_piste_2 = QtCore.QPointF(self.palkin_paatypiste, 200)
        nuoli_piste_3 = QtCore.QPointF(self.palkin_paatypiste + 7, 185)
        viiva_x = self.palkin_paatypiste
        self.viiva = QGraphicsLineItem(
            QtCore.QLineF(viiva_x, 100, viiva_x, 200))
        '''Luodaan nuoli QPolygonItem olio'''
        self.nuoli_3 = QGraphicsPolygonItem(
            QtGui.QPolygonF([nuoli_piste_1, nuoli_piste_2, nuoli_piste_3]))
        self.nuoli_brush = QtGui.QBrush(1)
        self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2)
        self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine)
        '''Lisätään viiva sekä päiden nuolet sceneen'''
        self.scene.addItem(self.viiva)
        self.scene.addItem(self.nuoli_3)
        self.viiva.hide()
        self.nuoli_3.hide()
        '''Lisätään tieto, että voima on asetettu'''
        Ominaisuudet.ulkoinen_voima(self, 1)

    def ulkoinen_voima_nuoli_alatuki(self):
        '''Nuolen kärkien koordinaatit alhaalta tuetulle palkille'''
        nuoli_piste_1 = QtCore.QPointF(self.palkin_keskipiste - 7, 185)
        nuoli_piste_2 = QtCore.QPointF(self.palkin_keskipiste, 200)
        nuoli_piste_3 = QtCore.QPointF(self.palkin_keskipiste + 7, 185)
        viiva_x = self.palkin_keskipiste
        '''Luodaan nuoli QPolygonItem olio'''
        self.nuoli_6 = QGraphicsPolygonItem(
            QtGui.QPolygonF([nuoli_piste_1, nuoli_piste_2, nuoli_piste_3]))
        self.nuoli_brush = QtGui.QBrush(1)
        self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2)
        self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine)

        self.viiva_5 = QGraphicsLineItem(
            QtCore.QLineF(viiva_x, 100, viiva_x, 200))
        '''Lisätään viiva sekä päiden nuolet sceneen'''
        self.scene.addItem(self.viiva_5)
        self.scene.addItem(self.nuoli_6)
        self.viiva_5.hide()
        self.nuoli_6.hide()
        '''Lisätään tieto, että voima on asetettu'''
        Ominaisuudet.ulkoinen_voima(self, 1)

    def paivita_voima(self):
        '''Lukee voiman arvon 
        ja kutsuu Ominaisuudet luoka metodia voima'''
        voima = self.sp_voima.value()
        Ominaisuudet.voima(self, voima)

    def paivita_yksikko_voima(self):
        '''Lukee ykiskön arvon 
        ja kutsuu Ominaisuudet-luokan metodia yksikko_voima'''
        self.yksikko_voima_arvo = self.yksikko_voima.currentData()
        Ominaisuudet.yksikko_voima(self, self.yksikko_voima_arvo)

    def materiaali_valikko(self):
        ''' Luo Materiaali-otsikon'''
        self.materiaali = QGraphicsSimpleTextItem("Materiaali")
        self.materiaali.setPos(
            0, 3 * self.button_height + 3 * self.button_separation)
        self.materiaali.font = QtGui.QFont()
        self.materiaali.font.setPointSize(12)
        self.materiaali.setFont(self.materiaali.font)
        self.scene.addItem(self.materiaali)
        '''Luo drop down valikon materiaalivalinnalle'''
        self.materiaali_valinta = QComboBox()
        self.materiaali_valinta.addItem("Teräs", 0)
        self.materiaali_valinta.addItem("Alumiini", 1)
        self.materiaali_valinta.addItem("Muovi", 2)
        self.materiaali_valinta.move(
            0, 3 * self.button_height + 3 * self.button_separation + 25)
        self.materiaali_valinta.resize(self.button_width,
                                       self.button_height - 25)
        self.materiaali_valinta.setEditable(True)
        self.materiaali_valinta.lineEdit().setAlignment(QtCore.Qt.AlignCenter)
        self.materiaali_valinta.setCurrentIndex(0)
        self.scene.addWidget(self.materiaali_valinta)
        self.materiaali_valinta.setEnabled(False)
        self.materiaali_valinta.currentIndexChanged.connect(
            self.paivita_materiaali)

    def paivita_materiaali(self):
        '''Lukee materiaalin arvon 
        ja kutsuu Ominaisuudet-luokan metodia materiaali'''
        materiaali = self.materiaali_valinta.currentData()
        Ominaisuudet.materiaali(self, materiaali)

    def simulaatio_nappi(self):
        '''Luo SIMULOI-napin'''
        self.simuloi = QPushButton('SIMULOI')
        self.simuloi.setToolTip('Simuloi valittu rakenne')
        self.simuloi.move(0,
                          4 * self.button_height + 4 * self.button_separation)
        self.simuloi.setStyleSheet("background-color:rgb(122, 201, 255)")
        self.simuloi.resize(self.button_width, self.button_height)
        self.simuloi.font = QtGui.QFont()
        self.simuloi.font.setPointSize(12)
        self.simuloi.setFont(self.simuloi.font)
        self.simuloi.setEnabled(False)
        self.simuloi.clicked.connect(self.simulaatio)
        self.scene.addWidget(self.simuloi)

    def simulaatio(self):
        '''Kutsuu laskentaa suorittavaa metodia ja tallentaa tuloksen.
        Tämän jälkeen kutsuu lopputuloksen esittävän tekstin päivittävää metodia
        sekä palkin visualisoivaa gradient-metodia'''
        Laskin.laskin(self)
        Ominaisuudet.palauta_tulos(self)
        self.paivita_tulos_teksti()
        self.tallennaAction.setEnabled(True)

        if Ominaisuudet.palauta_tuen_tyyppi(self) == 0:

            if Ominaisuudet.onko_ulkoinen_voima_asetettu(self) == 1:
                self.gradient_seina_tuki()

            if Ominaisuudet.onko_ulkoinen_voima_asetettu(self) == 0:
                self.gradient_seina_tuki_ei_voimaa()

        if Ominaisuudet.palauta_tuen_tyyppi(self) == 1:
            self.gradient_alatuki()

    def tulos_teksti(self):
        '''Lisää tekstin, joka kertoo maksimijänintyksen arvon'''
        teksti = "Maksimijännitys " + str(self.maks_jannitys) + " MPa"
        self.tulos = QGraphicsSimpleTextItem(teksti)
        self.tulos.setPos(550, 500)
        self.tulos.font = QtGui.QFont()
        self.tulos.font.setPointSize(12)
        self.tulos.setFont(self.tulos.font)
        self.scene.addItem(self.tulos)
        self.tulos.hide()

    def paivita_tulos_teksti(self):
        '''Päivittää maksimijännityksen arvoa kuvaavan tekstin'''
        maks_jannitys = Ominaisuudet.palauta_tulos(self)
        self.tulos.setText("Maksimijännitys " + str(maks_jannitys) + " MPa")
        self.tulos.show()

    def palkin_pituus_valikko(self):
        '''Luo palkin pituus tekstin sekä spinbox-valitsimen pituuden asettamista varten
        Päivittää palkin pituuden Ominaisuudet luokan avulla'''
        self.palkin_pituus = QGraphicsSimpleTextItem("Palkin pituus")
        self.palkin_pituus.setPos(300, 5)
        self.palkin_pituus.font = QtGui.QFont()
        self.palkin_pituus.font.setPointSize(12)
        self.palkin_pituus.setFont(self.palkin_pituus.font)
        self.scene.addItem(self.palkin_pituus)
        self.palkin_pituus.hide()

        self.sp = QSpinBox()
        self.scene.addWidget(self.sp)
        self.sp.hide()
        self.sp.move(450, 5)
        self.sp.setRange(0, 100)
        self.sp.setSingleStep(1)
        self.sp.setMinimumHeight(30)
        self.sp.setValue(int(Ominaisuudet.palauta_palkin_pituus(self)))
        self.paivita_pituus()
        self.sp.valueChanged.connect(self.paivita_pituus)

    def paivita_pituus(self):
        '''Lukee palkin pituuden ja aktivoi Ominaisuudet luokan meodin palkin pituus'''
        self.palkin_pituus_arvo = self.sp.value()
        Ominaisuudet.palkin_pituus(self, self.palkin_pituus_arvo)
        self.paivita_asteikon_arvot()

    def yksikko_pituus(self):
        '''Luo yksikönvalinta dropdown-menun
        ja arvon muuttuessa päivittää yksikön Ominaisuudet-luokassa'''
        self.yksikko = QComboBox()
        self.yksikko.addItem("m", 0)
        self.yksikko.addItem("cm", 1)
        self.yksikko.addItem("mm", 2)
        self.yksikko.move(500, 5)
        self.yksikko.setMinimumHeight(30)
        self.yksikko.setEditable(True)
        self.yksikko.lineEdit().setAlignment(QtCore.Qt.AlignCenter)
        self.yksikko.setCurrentIndex(
            Ominaisuudet.palauta_pituuden_yksikko(self))
        self.scene.addWidget(self.yksikko)
        self.yksikko.hide()
        self.yksikko_arvo = self.yksikko.currentData()
        self.yksikko.currentIndexChanged.connect(self.paivita_yksikko)

    def paivita_yksikko(self):
        '''Lukee yksikön arvon 
        ja kutsuu Ominaisuudet-luokan metodia yksikko'''
        self.yksikko_arvo = self.yksikko.currentData()
        Ominaisuudet.yksikko(self, self.yksikko_arvo)
        self.paivita_asteikon_arvot()

    def asteikko(self):
        ''''Luodaan viivaa kuvaava olio'''
        viiva = QtGui.QPen(QtCore.Qt.black, 2)
        viiva.setStyle(QtCore.Qt.SolidLine)
        '''Oikean puoleisen nuolen kärkien koordinaatit'''
        nuoli_1_piste_1 = QtCore.QPointF(990, 390)
        nuoli_1_piste_2 = QtCore.QPointF(1000, 400)
        nuoli_1_piste_3 = QtCore.QPointF(990, 410)
        '''Vasemman puoleisen nuolen kärkien koordinaatit'''
        nuoli_2_piste_1 = QtCore.QPointF(310, 390)
        nuoli_2_piste_2 = QtCore.QPointF(300, 400)
        nuoli_2_piste_3 = QtCore.QPointF(310, 410)
        '''Luodaan nuoli QPolygonF oliot'''
        self.nuoli_1 = QGraphicsPolygonItem(
            QtGui.QPolygonF(
                [nuoli_1_piste_1, nuoli_1_piste_2, nuoli_1_piste_3]))
        self.nuoli_2 = QGraphicsPolygonItem(
            QtGui.QPolygonF(
                [nuoli_2_piste_1, nuoli_2_piste_2, nuoli_2_piste_3]))

        self.nuoli_brush = QtGui.QBrush(1)
        self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2)
        self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine)

        self.line = QGraphicsLineItem(QtCore.QLineF(300, 400, 1000, 400))
        '''Lisätään viiva sekä päiden nuolet sceneen'''
        self.scene.addItem(self.line)
        self.scene.addItem(self.nuoli_1)
        self.scene.addItem(self.nuoli_2)
        self.line.hide()
        self.nuoli_1.hide()
        self.nuoli_2.hide()

    def lisaa_asteikko_arvo(self):
        '''Lisää tekstikentän pituuden arvolle sekä yksikölle'''
        teksti = (str(Ominaisuudet.palauta_palkin_pituus(self)) + " " + "m")
        self.asteikko_teksti = QGraphicsSimpleTextItem()
        self.asteikko_teksti.setText(teksti)
        self.asteikko_teksti.setPos(650, 425)
        self.asteikko_teksti.font = QtGui.QFont()
        self.asteikko_teksti.font.setPointSize(12)
        self.asteikko_teksti.setFont(self.asteikko_teksti.font)
        self.scene.addItem(self.asteikko_teksti)
        self.asteikko_teksti.hide()

    def paivita_asteikon_arvot(self):
        '''Päivittää palkin pituutta kuvaavan asteikon'''
        yksikko = Ominaisuudet.palauta_pituuden_yksikko(self)

        if yksikko == 0:
            self.yksikko_merkki = "m"
        if yksikko == 1:
            self.yksikko_merkki = "cm"
        if yksikko == 2:
            self.yksikko_merkki = "mm"

        pituus = float(Ominaisuudet.palauta_palkin_pituus(self))
        teksti = str(str(pituus) + " " + self.yksikko_merkki)
        self.asteikko_teksti.setText(teksti)
        self.asteikko_teksti.show()

    def gradient_seina_tuki(self):
        '''Luo seinästä tuetun palkin rasitusta kuvaavan gradientin'''
        gradient = QLinearGradient(300, 200, 300 + self.palkin_leveys, 200)
        gradient.setColorAt(0, QColor(244, 72, 66))
        gradient.setColorAt(1, QColor(65, 244, 83))
        self.rect.setBrush(gradient)

    def gradient_seina_tuki_ei_voimaa(self):
        '''Luo ilman ulkoista voimaa olevan gradientin'''
        gradient = QLinearGradient(300, 200, 300 + (self.palkin_leveys / 2),
                                   200)
        gradient.setColorAt(0, QColor(244, 72, 66))
        gradient.setColorAt(1, QColor(65, 244, 83))
        self.rect.setBrush(gradient)

    def gradient_alatuki(self):
        '''Luo kahdella alatuella olevan palkin rasitusta kuvaavan gradientin'''
        gradient = QLinearGradient(300, 200, 300 + self.palkin_leveys, 200)
        gradient.setColorAt(0, QColor(65, 244, 83))
        gradient.setColorAt(0.5, QColor(244, 72, 66))
        gradient.setColorAt(1, QColor(65, 244, 83))
        self.rect.setBrush(gradient)

    def nollaa_gradientti(self):
        '''Asettaa palkin "normaaliksi"'''
        self.rect.setBrush(QBrush(4))

    def uusi_rakenne(self):
        '''Muokkaa ikkunaa uuden simulaation luomista varten'''
        self.rect.hide()
        self.ulkoinen_voima.hide()
        self.sp_voima.hide()
        self.yksikko_voima.hide()
        self.nuoli_1.hide()
        self.nuoli_2.hide()
        self.nuoli_3.hide()
        self.nuoli_4.hide()
        self.nuoli_5.hide()
        self.nuoli_6.hide()
        self.viiva_1.hide()
        self.viiva_2.hide()
        self.viiva_3.hide()
        self.viiva_4.hide()
        self.viiva_5.hide()
        self.viiva.hide()
        self.palkin_pituus.hide()
        self.sp.hide()
        self.yksikko.hide()
        self.line.hide()
        self.asteikko_teksti.hide()
        self.tulos.hide()
        self.nollaa_gradientti()
        self.lisaa_tuki.show()
        self.vaihda_tuki.hide()
        self.poista_ulkoinen_voima.hide()
        self.lisaa_ulkoinen_voima.show()
        Ominaisuudet.alkuarvot(self)
        '''Asettaa napit'''
        self.uusi_palkki.setEnabled(True)
        self.lisaa_ulkoinen_voima.setEnabled(False)
        self.lisaa_tuki.setEnabled(False)
        self.simuloi.setEnabled(False)
        self.tallennaAction.setEnabled(False)
        '''Päivittää tuen tyypiksi arvon, joka vastaa, ettei tukea ole'''
        self.tuen_tyyppi = 2

    def close_application(self):
        '''sulkee ohjelman'''
        sys.exit()