Exemplo n.º 1
0
 def hide(self):
     " Handles the hiding of the panel and markers "
     for item in self.__markers:
         item.hide()
     QFrame.hide(self)
     self.parent().setReadOnly(False)
     self.parent().setFocus()
     return
Exemplo n.º 2
0
 def hide( self ):
     " Handles the hiding of the panel and markers "
     QFrame.hide( self )
     self.__text = None
     self.__paramPositions = None
     self.__highlightedParam = None
     self.__calltipLabel.setText( "" )
     return
Exemplo n.º 3
0
 def hide(self):
     " Handles the hiding of the panel and markers "
     QFrame.hide(self)
     self.__text = None
     self.__paramPositions = None
     self.__highlightedParam = None
     self.__calltipLabel.setText("")
     return
Exemplo n.º 4
0
 def hide( self ):
     " Handles the hiding of the panel and markers "
     for item in self.__markers:
         item.hide()
     QFrame.hide( self )
     self.parent().setReadOnly( False )
     self.parent().setFocus()
     return
Exemplo n.º 5
0
    def __init__(self, parent):
        QFrame.__init__(self, parent)

        # Make the frame nice looking
        palette = self.palette()
        palette.setColor(self.backgroundRole(), GlobalData().skin.calltipPaper)
        self.setPalette(palette)

        self.setFrameShape(QFrame.StyledPanel)
        self.setLineWidth(2)
        self.setAutoFillBackground(True)

        # Keep pylint happy
        self.__calltipLabel = None
        self.__text = None
        self.__paramPositions = None
        self.__highlightedParam = None

        self.__createLayout()
        QFrame.hide(self)
        self.setFocusPolicy(Qt.NoFocus)
        return
Exemplo n.º 6
0
    def __init__( self, parent ):
        QFrame.__init__( self, parent )

        # Make the frame nice looking
        palette = self.palette()
        palette.setColor( self.backgroundRole(),
                          GlobalData().skin.calltipPaper )
        self.setPalette( palette )

        self.setFrameShape( QFrame.StyledPanel )
        self.setLineWidth( 2 )
        self.setAutoFillBackground( True )

        # Keep pylint happy
        self.__calltipLabel = None
        self.__text = None
        self.__paramPositions = None
        self.__highlightedParam = None

        self.__createLayout()
        QFrame.hide( self )
        self.setFocusPolicy( Qt.NoFocus )
        return
Exemplo n.º 7
0
class Request_Handler( QNetworkReply ):

    # =======================================================================
    def __init__(self, parent=None):

        # -------------------------------------------------------------------
        # http://pyqt.sourceforge.net/Docs/PyQt4/qnetworkreply.html
        QNetworkReply.__init__( self, parent );
        # -------------------------------------------------------------------
        self.PARENT                                 = parent;
        self.DEBUG                                  = False;
        self.LOG_TAG                                = str(self.__class__.__name__).upper();
        
        # -------------------------------------------------------------------
        self.HEADERS_FRAME                          = QFrame( self.PARENT );
        self.HEADERS_FRAME.setGeometry( 10, 50, 980, 520 );
        self.HEADERS_FRAME.setStyleSheet( "QFrame{ font: 12px 'monospace'; color: #000; background-color: rbga(0,0,0, 170); border-style: solid; border-width: 5px; border-color: #FFF; }" );
        self.HEADERS_FRAME.hide();

        # -------------------------------------------------------------------
        self.HEADERS_RAW                            = QTextEdit("TEST: TEST", self.HEADERS_FRAME);
        self.HEADERS_RAW.setGeometry(5, 5, 970, 510);
        self.HEADERS_RAW.setStyleSheet("QTextEdit{  color: #fff; margin: 5px; padding: 5px; border-style: dashed; border-width: 1px; border-color: #FFF; }")
        self.HEADERS_RAW.setReadOnly(True);
        #self.HEADERS_RAW.setOpenExternalLinks(True);
        #self.HEADERS_RAW.show();

        # -------------------------------------------------------------------
        self.downloadProgress.connect( self._DOWN );
        self.uploadProgress.connect( self._UP );

        self.error.connect( self._ERROR);
        self.finished.connect( self._FINISH );
        self.metaDataChanged.connect( self._META_DATA_CHANGED );
        self.sslErrors.connect( self._SSL_ERROR );

        # -------------------------------------------------------------------
        self.SHOW_NOT_REQUESTED_HEADERS             = False; 
        self.LAST_PAGE_REQUEST_HEADERS              = {};
        self.LAST_PAGE_REQUEST_FILES                = [];


        # -------------------------------------------------------------------
        self.IS_OPEN                                = False;
        self.KEEP_OPEN                              = False;

        # -------------------------------------------------------------------
        self.PARENT.SPLASH.STATUS( self.LOG_TAG+": [INIT]" );

        # -------------------------------------------------------------------

    # =======================================================================
    def CMD( self, _CMD ):

        # -------------------------------------------------------------------
        #__exec:request_handler:headers:show
        #__exec:request_handler:headers:hide
        #__exec:request_handler:headers:keep_open:(0|1)
        # -------------------------------------------------------------------
        if self.DEBUG:
            print( )
        # -------------------------------------------------------------------
        try:
    
            # -----------------------------------------------
            if _CMD[0] == "headers":

                if _CMD[1] == "show":
                    self.SHOW_HEADERS( );

                elif _CMD[1] == "hide":
                    self.HIDE_HEADERS( );

                elif _CMD[1] == "keep_open":
                    self.KEEP_OPEN = True if _CMD[2] == "1" else False;
                    self.LOCAL_INFO_LOG( "request_handler:headers:keep_open:"+_CMD[2] );

                return;
            # -----------------------------------------------
            if _CMD[0] == "cmd":
                pass;
                    
            # -----------------------------------------------

        except Exception as _err:
            self.LOCAL_ERROR_LOG( str(_CMD)+" | "+str(_err) );
    
        # -------------------------------------------------------------------

    # =======================================================================
    def SHOW_HEADERS( self ):

        # -------------------------------------------------------------------
        try:

            out = '["'+self.PARENT.LAST_URL_ADDR+']"\n';

            for _key in self.LAST_PAGE_REQUEST_HEADERS:
                _l = "-----------------------------------------------------------------------\n"
                _l += '["'+_key+'"] => \n["'+self.LAST_PAGE_REQUEST_HEADERS[ _key ]+'"]'+"\n"
                #print( "L: "+_l );
                out += _l;

            self.HEADERS_RAW.setText( out );
            self.HEADERS_FRAME.show();

            self.IS_OPEN = True;


        except Exception as _err:
            self.LOCAL_ERROR_LOG( "'Can't show header: "+str(_err) );

        # -------------------------------------------------------------------

    # =======================================================================
    def HIDE_HEADERS( self ):

        # -------------------------------------------------------------------
        self.HEADERS_FRAME.hide();
        self.IS_OPEN = False;

        # -------------------------------------------------------------------

    # =======================================================================
    def REQUEST_FINISHED( self, _netReplay ):

        # -------------------------------------------------------------------
        # _raw_header_list.size();    # -> size in bytes && array.data()[ size() ]; // returns '\0'
        # _raw_header_list.length();  # -> same as size()
        # _raw_header_list.count();   # -> Returns the number of (potentially overlapping) occurrences of string str in the byte array.
        # _raw_header_list.mid(5, 20);# -> get mid piece
        # _raw_header_list.data()[0]; # -> char At 0
        # _raw_header_list.data();    # -> string
        """
        QByteArray x("Five pineapples");
        QByteArray y = x.mid(5, 4);     // y == "pine"
        QByteArray z = x.mid(5);        // z == "pineapples"
        """
        # -------------------------------------------------------------------
        #del self.LAST_PAGE_REQUEST_HEADERS;
        #self.LAST_PAGE_REQUEST_HEADERS = {};

        #del self.LAST_PAGE_REQUEST_FILES;
        #self.LAST_PAGE_REQUEST_FILES = [];

        # -------------------------------------------------------------------
        _req_url = str( _netReplay.url().toString() );
        #print('["'+_req_url+'"]')

        # -------------------------------------------------------------------
        try:
            
            # ---------------------------------------------------------------
            if not self.SHOW_NOT_REQUESTED_HEADERS:

                if _req_url == self.PARENT.LAST_URL_ADDR:

                    if self.IS_OPEN:

                        _raw_header_list = _netReplay.rawHeaderList();

                        del self.LAST_PAGE_REQUEST_HEADERS;
                        self.LAST_PAGE_REQUEST_HEADERS = {};
                        
                        for _header in _raw_header_list:
                            self.LAST_PAGE_REQUEST_HEADERS[ str(_header) ] = str(_netReplay.rawHeader( _header ) );
                            #print( 'HEAD: ["'+_header+'"] : ["'+_netReplay.rawHeader( _header ) +'"]' );

                        self.SHOW_HEADERS();


            # ---------------------------------------------------------------
            else:

                print("-----------------------------------------------------------");
                print('REQ_URL: ["'+_req_url+'"] : [FETCHING]');
                _raw_header_list = _netReplay.rawHeaderList();

                del self.LAST_PAGE_REQUEST_HEADERS;
                self.LAST_PAGE_REQUEST_HEADERS = {};

                for _header in _raw_header_list:
                    print( 'HEAD: ["'+_header+'"] : ["'+_netReplay.rawHeader( _header ) +'"]' );
                    self.LAST_PAGE_REQUEST_HEADERS[ str(_header) ] = str(_netReplay.rawHeader( _header ) );

            # ---------------------------------------------------------------

        except Exception as _err:
            self.LOCAL_ERROR_LOG( str(_err) );


        # -------------------------------------------------------------------
        if int(_netReplay.error()) == 1:
            _msg = "ERR_NUM: ["+str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]";
            print( "["+self.PARENT.GET_DATE_TIME()+"]:["+_msg+"]" );

        elif int(_netReplay.error()) == 3:
            self.LOCAL_WARNING_LOG( str(_netReplay.errorString()) );

        return;

        # -------------------------------------------------------------------
        if _netReplay.error() == QNetworkReply.NoError: # .error() == 0
            # Note: When the HTTP protocol returns a redirect no error will be reported. You can check if there is a redirect with the QNetworkRequest.RedirectionTargetAttribute attribute.
            #print("NoError: # .error() == 0");
            pass;

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 1
            #the remote server refused the connection (the server is not accepting requests)
            print("ConnectionRefusedError: # .error() == 1");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 2
            # the remote server closed the connection prematurely, before the entire reply was received and processed
            print("ConnectionRefusedError: # .error() == 2");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() ==  3
            # the remote host name was not found (invalid hostname)
            print("ConnectionRefusedError: # .error() ==  3");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 4
            # the connection to the remote server timed out
            print("ConnectionRefusedError: # .error() == 4");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 5
            # the operation was canceled via calls to abort() or close() before it was finished.
            print("ConnectionRefusedError: # .error() == 5");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 6
            # the SSL/TLS handshake failed and the encrypted channel could not be established. The sslErrors() signal should have been emitted.
            print("ConnectionRefusedError: # .error() == 6");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 7
            # the connection was broken due to disconnection from the network, however the system has initiated roaming to another access point. The request should be resubmitted and will be processed as soon as the connection is re-established.
            print("ConnectionRefusedError: # .error() == 7");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 101
            # the connection to the proxy server was refused (the proxy server is not accepting requests)
            print("ConnectionRefusedError: # .error() == 101");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 102
            # the proxy server closed the connection prematurely, before the entire reply was received and processed
            print("ConnectionRefusedError: # .error() == 102");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 103
            # the proxy host name was not found (invalid proxy hostname)
            print("ConnectionRefusedError: # .error() == 103");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 104
            # connection to the proxy timed out or the proxy did not reply in time to the request sent
            print("ConnectionRefusedError: # .error() == 104");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 105
            # the proxy requires authentication in order to honour the request but did not accept any credentials offered (if any)
            print("ConnectionRefusedError: # .error() == 105");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 201
            # the access to the remote content was denied (similar to HTTP error 401)
            print("ConnectionRefusedError: # .error() == 201");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 202
            # the operation requested on the remote content is not permitted
            print("ConnectionRefusedError: # .error() == 202");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 203
            # the remote content was not found at the server (similar to HTTP error 404)
            print("ConnectionRefusedError: # .error() == 203");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 204
            # the remote server requires authentication to serve the content but the credentials provided were not accepted (if any)
            print("ConnectionRefusedError: # .error() == 204");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 205
            # the request needed to be sent again, but this failed for example because the upload data could not be read a second time.
            print("ConnectionRefusedError: # .error() == 205");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 301
            # the Network Access API cannot honor the request because the protocol is not known
            print("ConnectionRefusedError: # .error() == 301");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 302
            # the requested operation is invalid for this protocol
            print("ConnectionRefusedError: # .error() == 302");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 99
            # an unknown network-related error was detected
            print("ConnectionRefusedError: # .error() == 99");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 199
            # an unknown proxy-related error was detected
            print("ConnectionRefusedError: # .error() == 199");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 299
            # an unknown error related to the remote content was detected
            print("ConnectionRefusedError: # .error() == 299");

        elif _netReplay.error() == QNetworkReply.ConnectionRefusedError: # .error() == 399
            # 399 a breakdown in protocol was detected (parsing error, invalid or unexpected responses, etc.)
            print("ConnectionRefusedError: # .error() == 399");

        # -------------------------------------------------------------------
        _msg = None;

        if _netReplay.error() == 0:
            #print("ERR_NUM: ["+str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]");
            pass;

        elif _netReplay.error() == 1:
            _msg = "ERR_NUM: ["+str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]";

        elif _netReplay.error() == 2:
            _msg = "ERR_NUM: ["+str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]";

        elif _netReplay.error() == 3: # Host [host-name] not found
            _msg = "ERR_NUM: ["+str(_netReplay.error())+"]";
            _msg += str(_netReplay.errorString()).replace("Host ","Host ['").replace(" not found","'] not found!");

        elif _netReplay.error() == 5: # [5, Operation canceled]
            #print("ERR_NUM: ["+str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]");
            pass;

        elif _netReplay.error() == 6: # [6, SSL handshake failed]
            _msg = "ERR_NUM: ["+str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]";

        elif _netReplay.error() == 202: # Error downloading 
            _msg = "ERR_NUM: ["+str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]";

        else:
            _msg = str(_netReplay.error())+", "+str( _netReplay.errorString() )+"]";


        # -------------------------------------------------------------------
        if _msg is not None:
            self.LOCAL_ERROR_LOG( str(_netReplay.error())+", "+_msg+", "+str(_netReplay.readAll().data()) );

        # -------------------------------------------------------------------

        """
        data = json.loads(str(_QNetworkReply.readAll().data())) # get data
        str( _QNetworkReply.readAll().data() );
        """
        # -------------------------------------------------------------------

    # =======================================================================
    def _DOWN(self, _a, _b):

        self.LOCAL_ERROR_LOG( "DOWN]: "+str(_a)+", "+str(_b));

    def _UP(self, _a, _b):
        self.LOCAL_ERROR_LOG( "UP]: "+str(_a)+", "+str(_b));

    def _ERROR(self, _error):
        self.LOCAL_ERROR_LOG( "_ERROR]: "+str(_error));

    def _FINISH(self):
        self.LOCAL_ERROR_LOG( "_FINISH]: NO-ARGS");

    def _META_DATA_CHANGED(self):
        self.LOCAL_ERROR_LOG( "_META_DATA_CHANGED]: NO-ARGS");

    def _SSL_ERROR(self, _ssl_error_list):
        self.LOCAL_ERROR_LOG( "_SSL_ERROR]: "+str(_ssl_error_list));

    # =======================================================================
    def LOCAL_INFO_LOG( self, _msg, METHOD=None ):

        # -------------------------------------------------------------------
        if METHOD is None:
            self.PARENT.LOCAL_INFO_LOG( "['"+self.LOG_TAG+"']: ["+_msg+"]" );
        else:
            self.PARENT.LOCAL_INFO_LOG( "['"+self.LOG_TAG+"."+METHOD+"']: ["+_msg+"]" );
        # -------------------------------------------------------------------

    # =======================================================================
    def LOCAL_ERROR_LOG( self, _msg, METHOD=None ):

        # -------------------------------------------------------------------
        if self.DEBUG or self.PARENT.DEBUG_GLOBAL: self.PARENT.DEBUGGER.DEBUG();
        # -------------------------------------------------------------------
        if METHOD is None:
            self.PARENT.LOCAL_ERROR_LOG( "['"+self.LOG_TAG+"']: ["+_msg+"]" );
        else:
            self.PARENT.LOCAL_ERROR_LOG( "['"+self.LOG_TAG+"."+METHOD+"']: ["+_msg+"]" );
        # -------------------------------------------------------------------

    # =======================================================================
    def LOCAL_WARNING_LOG( self, _msg, METHOD=None ):

        # -------------------------------------------------------------------
        #if self.DEBUG or self.PARENT.DEBUG_GLOBAL: self.PARENT.DEBUGGER.DEBUG();
        # -------------------------------------------------------------------
        if METHOD is None:
            self.PARENT.LOCAL_WARNING_LOG( "['"+self.LOG_TAG+"']: ["+_msg+"]" );
        else:
            self.PARENT.LOCAL_WARNING_LOG( "['"+self.LOG_TAG+"."+METHOD+"']: ["+_msg+"]" );
class Progress(QDialog):
    file_converted_signal = pyqtSignal()
    refr_bars_signal = pyqtSignal(int)
    update_text_edit_signal = pyqtSignal(str)

    def __init__(self, files, tab, delete, parent, test=False):
        """
        Keyword arguments:
        files  -- list with dicts containing file names
        tab -- instanseof AudioVideoTab, ImageTab or DocumentTab
               indicating currently active tab
        delete -- boolean that shows if files must removed after conversion
        parent -- parent widget

        files:
        Each dict have only one key and one corresponding value.
        Key is a file to be converted and it's value is the name of the new
        file that will be converted.

        Example list:
        [{"/foo/bar.png" : "/foo/bar.bmp"}, {"/f/bar2.png" : "/f/bar2.bmp"}]
        """
        super(Progress, self).__init__(parent)
        self.parent = parent

        self.files = files
        self.num_total_files = len(self.files)
        self.tab = tab
        self.delete = delete
        if not test:
            self._type = tab.name
            self.step = int(100 / len(files))
        self.ok = 0
        self.error = 0
        self.running = True

        self.nowQL = QLabel(self.tr('In progress: '))
        totalQL = QLabel(self.tr('Total:'))
        self.nowQPBar = QProgressBar()
        self.nowQPBar.setValue(0)
        self.totalQPBar = QProgressBar()
        self.totalQPBar.setValue(0)
        self.cancelQPB = QPushButton(self.tr('Cancel'))

        detailsQPB = QCommandLinkButton(self.tr('Details'))
        detailsQPB.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        detailsQPB.setCheckable(True)
        detailsQPB.setMaximumWidth(113)
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.outputQTE = QTextEdit()
        self.outputQTE.setReadOnly(True)
        self.frame = QFrame()
        frame_layout = utils.add_to_layout('h', self.outputQTE)
        self.frame.setLayout(frame_layout)
        self.frame.hide()

        hlayout = utils.add_to_layout('h', None, self.nowQL, None)
        hlayout2 = utils.add_to_layout('h', None, totalQL, None)
        hlayout3 = utils.add_to_layout('h', detailsQPB, line)
        hlayout4 = utils.add_to_layout('h', self.frame)
        hlayout5 = utils.add_to_layout('h', None, self.cancelQPB)
        vlayout = utils.add_to_layout('v', hlayout, self.nowQPBar, hlayout2,
                                      self.totalQPBar, None, hlayout3,
                                      hlayout4, hlayout5)
        self.setLayout(vlayout)

        detailsQPB.toggled.connect(self.resize_dialog)
        detailsQPB.toggled.connect(self.frame.setVisible)
        self.cancelQPB.clicked.connect(self.reject)
        self.file_converted_signal.connect(self.next_file)
        self.refr_bars_signal.connect(self.refresh_progress_bars)
        self.update_text_edit_signal.connect(self.update_text_edit)

        self.resize(484, 200)
        self.setWindowTitle('FF Multi Converter - ' + self.tr('Conversion'))

        if not test:
            self.get_data()  # should be first and not in QTimer.singleShot()
            QTimer.singleShot(0, self.manage_conversions)

    def get_data(self):
        """Collect conversion data from parents' widgets."""
        if self._type == 'AudioVideo':
            self.cmd = self.tab.commandQLE.text()
        elif self._type == 'Images':
            width = self.tab.widthQLE.text()
            self.size = ''
            self.mntaspect = False
            if width:
                height = self.tab.heightQLE.text()
                self.size = '{0}x{1}'.format(width, height)
                self.mntaspect = self.tab.imgaspectQChB.isChecked()
            self.imgcmd = self.tab.commandQLE.text()
            if self.tab.autocropQChB.isChecked():
                self.imgcmd += ' -trim +repage'
            rotate = self.tab.rotateQLE.text().strip()
            if rotate:
                self.imgcmd += ' -rotate {0}'.format(rotate)
            if self.tab.vflipQChB.isChecked():
                self.imgcmd += ' -flip'
            if self.tab.hflipQChB.isChecked():
                self.imgcmd += ' -flop'

    def resize_dialog(self):
        """Resize dialog."""
        height = 200 if self.frame.isVisible() else 366
        self.setMinimumSize(484, height)
        self.resize(484, height)

    def update_text_edit(self, txt):
        """Append txt to the end of current self.outputQTE's text."""
        current = self.outputQTE.toPlainText()
        self.outputQTE.setText(current + txt)
        self.outputQTE.moveCursor(QTextCursor.End)

    def refresh_progress_bars(self, now_percent):
        """Refresh the values of self.nowQPBar and self.totalQPBar."""
        total_percent = int(((now_percent * self.step) / 100) + self.min_value)

        if now_percent > self.nowQPBar.value() and not (now_percent > 100):
            self.nowQPBar.setValue(now_percent)
        if (total_percent > self.totalQPBar.value()
                and not (total_percent > self.max_value)):
            self.totalQPBar.setValue(total_percent)

    def manage_conversions(self):
        """
        Check whether all files have been converted.
        If not, it will allow convert_a_file() to convert the next file.
        """
        if not self.running:
            return
        if not self.files:
            self.totalQPBar.setValue(100)
        if self.totalQPBar.value() >= 100:
            sum_files = self.ok + self.error
            msg = QMessageBox(self)
            msg.setStandardButtons(QMessageBox.Ok)
            msg.setWindowTitle(self.tr("Report"))
            msg.setText(
                self.tr("Converted: {0}/{1}".format(self.ok, sum_files)))
            msg.setModal(False)
            msg.show()

            self.cancelQPB.setText(self.tr("Close"))
        else:
            self.convert_a_file()

    def next_file(self):
        """
        Update progress bars values, remove converted file from self.files
        and call manage_conversions() to continue the process.
        """
        self.totalQPBar.setValue(self.max_value)
        self.nowQPBar.setValue(100)
        QApplication.processEvents()
        self.files.pop(0)
        self.manage_conversions()

    def reject(self):
        """
        Use standard dialog to ask whether procedure must stop or not.
        Use the SIGSTOP to stop the conversion process while waiting for user
        to respond and SIGCONT or kill depending on user's answer.
        """
        if not self.files:
            QDialog.accept(self)
            return
        if self._type == 'AudioVideo':
            self.process.send_signal(signal.SIGSTOP)
        self.running = False
        reply = QMessageBox.question(
            self, 'FF Multi Converter - ' + self.tr('Cancel Conversion'),
            self.tr('Are you sure you want to cancel conversion?'),
            QMessageBox.Yes | QMessageBox.Cancel)
        if reply == QMessageBox.Yes:
            if self._type == 'AudioVideo':
                self.process.kill()
            self.running = False
            self.thread.join()
            QDialog.reject(self)
        if reply == QMessageBox.Cancel:
            self.running = True
            if self._type == 'AudioVideo':
                self.process.send_signal(signal.SIGCONT)
            else:
                self.manage_conversions()

    def convert_a_file(self):
        """
        Update self.nowQL's text with current file's name, set self.nowQPBar
        value to zero and start the conversion procedure in a second thread
        using threading module.
        """
        if not self.files:
            return
        from_file = list(self.files[0].keys())[0]
        to_file = list(self.files[0].values())[0]

        text = os.path.basename(from_file[1:-1])
        num_file = self.num_total_files - len(self.files) + 1
        text += ' ({0}/{1})'.format(num_file, self.num_total_files)

        self.nowQL.setText(self.tr('In progress:') + ' ' + text)
        self.nowQPBar.setValue(0)

        self.min_value = self.totalQPBar.value()
        self.max_value = self.min_value + self.step

        if not os.path.exists(from_file[1:-1]):
            self.error += 1
            self.file_converted_signal.emit()
            return

        def convert():
            if self._type == 'AudioVideo':
                conv_func = self.convert_video
                params = (from_file, to_file, self.cmd)
            elif self._type == 'Images':
                conv_func = self.convert_image
                params = (from_file, to_file, self.size, self.mntaspect,
                          self.imgcmd)
            else:
                conv_func = self.convert_document
                params = (from_file, to_file)

            if conv_func(*params):
                self.ok += 1
                if self.delete and not from_file == to_file:
                    try:
                        os.remove(from_file[1:-1])
                    except OSError:
                        pass
            else:
                self.error += 1

            self.file_converted_signal.emit()

        self.thread = threading.Thread(target=convert)
        self.thread.start()

    def convert_video(self, from_file, to_file, command):
        """
        Create the ffmpeg command and execute it in a new process using the
        subprocess module. While the process is alive, parse ffmpeg output,
        estimate conversion progress using video's duration.
        With the result, emit the corresponding signal in order progressbars
        to be updated. Also emit regularly the corresponding signal in order
        an outputQTE to be updated with ffmpeg's output. Finally, save log
        information.

        Return True if conversion succeed, else False.
        """
        # note: from_file and to_file names are inside quotation marks
        convert_cmd = '{0} -y -i {1} {2} {3}'.format(self.parent.vidconverter,
                                                     from_file, command,
                                                     to_file)
        self.update_text_edit_signal.emit(convert_cmd + '\n')

        self.process = subprocess.Popen(shlex.split(convert_cmd),
                                        stderr=subprocess.STDOUT,
                                        stdout=subprocess.PIPE)

        final_output = myline = ''
        reader = io.TextIOWrapper(self.process.stdout, encoding='utf8')
        while True:
            out = reader.read(1)
            if out == '' and self.process.poll() is not None:
                break
            myline += out
            if out in ('\r', '\n'):
                m = re.search("Duration: ([0-9:.]+)", myline)
                if m:
                    total = utils.duration_in_seconds(m.group(1))
                n = re.search("time=([0-9:]+)", myline)
                # time can be of format 'time=hh:mm:ss.ts' or 'time=ss.ts'
                # depending on ffmpeg version
                if n:
                    time = n.group(1)
                    if ':' in time:
                        time = utils.duration_in_seconds(time)
                    now_sec = int(float(time))
                    try:
                        self.refr_bars_signal.emit(100 * now_sec / total)
                    except (UnboundLocalError, ZeroDivisionError):
                        pass
                self.update_text_edit_signal.emit(myline)
                final_output += myline
                myline = ''
        self.update_text_edit_signal.emit('\n\n')

        return_code = self.process.poll()

        log_data = {
            'command': convert_cmd,
            'returncode': return_code,
            'type': 'VIDEO'
        }
        log_lvl = logging.info if return_code == 0 else logging.error
        log_lvl(final_output, extra=log_data)

        return return_code == 0

    def convert_image(self, from_file, to_file, size, mntaspect, imgcmd):
        """
        Convert an image using ImageMagick.
        Create conversion info ("cmd") and emit the corresponding signal
        in order an outputQTE to be updated with that info.
        Finally, save log information.

        Return True if conversion succeed, else False.
        """
        # note: from_file and to_file names are inside quotation marks
        resize = ''
        if size:
            resize = '-resize {0}'.format(size)
            if not mntaspect:
                resize += '\!'

        imgcmd = ' ' + imgcmd.strip() + ' '
        cmd = 'convert {0} {1}{2}{3}'.format(from_file, resize, imgcmd,
                                             to_file)
        self.update_text_edit_signal.emit(cmd + '\n')
        child = subprocess.Popen(shlex.split(cmd),
                                 stderr=subprocess.STDOUT,
                                 stdout=subprocess.PIPE)
        child.wait()

        reader = io.TextIOWrapper(child.stdout, encoding='utf8')
        final_output = reader.read()
        self.update_text_edit_signal.emit(final_output + '\n\n')

        return_code = child.poll()

        log_data = {'command': cmd, 'returncode': return_code, 'type': 'IMAGE'}
        log_lvl = logging.info if return_code == 0 else logging.error
        log_lvl(final_output, extra=log_data)

        return return_code == 0

    def convert_document(self, from_file, to_file):
        """
        Create the unoconv command and execute it using the subprocess module.

        Emit the corresponding signal in order an outputQTE to be updated
        with unoconv's output. Finally, save log information.

        Return True if conversion succeed, else False.
        """
        # note: from_file and to_file names are inside quotation marks
        to_base, to_ext = os.path.splitext(to_file[1:-1])

        cmd = 'unoconv -f {0} -o {1} {2}'.format(to_ext[1:], to_file,
                                                 from_file)
        self.update_text_edit_signal.emit(cmd + '\n')
        child = subprocess.Popen(shlex.split(cmd),
                                 stderr=subprocess.STDOUT,
                                 stdout=subprocess.PIPE)
        child.wait()

        reader = io.TextIOWrapper(child.stdout, encoding='utf8')
        final_output = reader.read()
        self.update_text_edit_signal.emit(final_output + '\n\n')

        return_code = child.poll()

        log_data = {
            'command': cmd,
            'returncode': return_code,
            'type': 'DOCUMENT'
        }
        log_lvl = logging.info if return_code == 0 else logging.error
        log_lvl(final_output, extra=log_data)

        return return_code == 0
Exemplo n.º 9
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = False
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        # 'current_context' MUST be the first thing assigned to a widget
        self.current_context = settings.Context()
        if self.settingsHandler:
            stored_settings = kwargs.get("stored_settings", None)
            self.settingsHandler.initialize(self, stored_settings)

        # number of control signals that are currently being processed
        # needed by signalWrapper to know when everything was sent
        self.needProcessing = 0  # used by signalManager
        self.signalManager = kwargs.get("signal_manager", None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self._guiElements = []  # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        # if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        # TODO: kill me
        self.__dict__.update(environ.directories)

        if self.name:
            self.setCaption(self.name.replace("&", ""))
        self.setFocusPolicy(Qt.StrongFocus)

        self.wrappers = []  # stored wrappers for widget events
        self.linksIn = {}  # signalName : (dirty, widFrom, handler, signalData)
        self.linksOut = {}  # signalName: (signalData, id)
        self.connections = {}  # keys are (control, signal) and values are
        # wrapper instances. Used in connect/disconnect
        self.callbackDeposit = []
        self.startTime = time.time()  # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return Qt.Window if cls.resizing_enabled else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint

    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.topWidgetPart = gui.widgetBox(self, orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart, orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(
                self.topWidgetPart,
                orientation="vertical",
                sizePolicy=QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding),
                margin=0,
            )
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart, orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart, orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground, self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea, os.path.join(self.widgetDir, "icons/triangle-orange.png")
            )
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea, os.path.join(self.widgetDir + "icons/triangle-red.png")
            )

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning), ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning, self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = -self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    def getIconNames(self, iconName):
        # if canvas sent us a prepared list of valid names, just return those
        if type(iconName) == list:
            return iconName

        names = []
        name, ext = os.path.splitext(iconName)
        for num in [16, 32, 42, 60]:
            names.append("%s_%d%s" % (name, num, ext))
        fullPaths = []
        module_dir = os.path.dirname(sys.modules[self.__module__].__file__)
        for paths in [(self.widgetDir, name), (self.widgetDir, "icons", name), (module_dir, "icons", name)]:
            for name in names + [iconName]:
                fname = os.path.join(*paths)
                if os.path.exists(fname):
                    fullPaths.append(fname)
            if fullPaths != []:
                break

        if len(fullPaths) > 1 and fullPaths[-1].endswith(iconName):
            # if we have the new icons we can remove the default icon
            fullPaths.pop()
        return fullPaths

    def setWidgetIcon(self, iconName):
        iconNames = self.getIconNames(iconName)
        icon = QIcon()
        for name in iconNames:
            pix = QPixmap(name)
            icon.addPixmap(pix)

        self.setWindowIcon(icon)

    # ##############################################
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." % str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1

    # call processEvents(), but first remember position and size of widget in
    # case one of the events would be move or resize
    # call this function if needed in __init__ of the widget
    def safeProcessEvents(self):
        keys = ["widgetShown"]
        vals = [(key, getattr(self, key, None)) for key in keys]
        qApp.processEvents()
        for (key, val) in vals:
            if val != None:
                setattr(self, key, val)

    # this function is called at the end of the widget's __init__ when the
    # widgets is saving its position and size parameters
    def restoreWidgetPosition(self):
        if self.save_position:
            geometry = getattr(self, "savedWidgetGeometry", None)
            restored = False
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                # Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)

    # this is called in canvas when loading a schema. it opens the widgets
    # that were shown when saving the schema
    def restoreWidgetStatus(self):
        if self.save_position and getattr(self, "widgetShown", None):
            self.show()

    # when widget is resized, save new width and height into widgetWidth and
    # widgetHeight. some widgets can put this two variables into settings and
    # last widget shape is restored after restart
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives the resizeEvent before showEvent and we must not
        # overwrite the the savedGeometry before then)
        if self.save_position and self.isVisible():
            self.savedWidgetGeometry = str(self.saveGeometry())

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.widgetShown = 0
            self.savedWidgetGeometry = str(self.saveGeometry())
        QDialog.hideEvent(self, ev)

    # set widget state to shown
    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            self.widgetShown = 1
        self.restoreWidgetPosition()

    def closeEvent(self, ev):
        if self.save_position:
            self.savedWidgetGeometry = str(self.saveGeometry())
        QDialog.closeEvent(self, ev)

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        if self.parent != None and isinstance(self.parent, QTabWidget):
            self.parent.setTabText(self.parent.indexOf(self), caption)
        else:
            # we have to save caption title in case progressbar will change it
            self.captionTitle = str(caption)
            self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        if self.current_context is not None:
            self.settingsHandler.close_context(self)
        self.current_context = None

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activateLoadedSettings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def setOptions(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self):
        self.progressBarValue = 0
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")
        self.processingStateChanged.emit(1)

    def progressBarSet(self, value):
        if value > 0:
            self.__progressBarValue = value
            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle + " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        self.progressBarValueChanged.emit(value)

        qApp.processEvents()

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet, fget=progressBarValue)

    def progressBarAdvance(self, value):
        self.progressBarSet(self.progressBarValue + value)

    def progressBarFinished(self):
        self.setWindowTitle(self.captionTitle)
        self.processingStateChanged.emit(0)

    def openWidgetHelp(self):
        if "widgetInfo" in self.__dict__:  # This widget is on a canvas.
            qApp.canvasDlg.helpWindow.showHelpFor(self.widgetInfo, True)

    def keyPressEvent(self, e):
        if e.key() in (Qt.Key_Help, Qt.Key_F1):
            self.openWidgetHelp()
        #    e.ignore()
        elif (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)
        # self.setState("Warning", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, stateType, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[stateType]:
                    self.widgetState[stateType].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[stateType]:
                    self.widgetState[stateType].pop(id)
                    changed = 1
            else:
                self.widgetState[stateType][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(stateType, i, "")
            else:
                self.widgetStateChanged.emit(stateType, id, text or "")
        return changed

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {
            "Info": "canvasIcons:information.png",
            "Warning": "canvasIcons:warning.png",
            "Error": "canvasIcons:error.png",
        }
        for show, what in [(info, "Info"), (warning, "Warning"), (error, "Error")]:
            if show and self.widgetState[what]:
                items.append(
                    '<img src="%s" style="float: left;"> %s'
                    % (iconPath[what], "\n".join(self.widgetState[what].values()))
                )
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath("canvasIcons", os.path.join(environ.canvas_install_dir, "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M): lambda self: self.showMaximized
            if self.isMinimized()
            else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W): lambda self: self.setVisible(not self.isVisible()),
        }

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Exemplo n.º 10
0
class Progress(QDialog):
    file_converted_signal = pyqtSignal()
    refr_bars_signal = pyqtSignal(int)
    update_text_edit_signal = pyqtSignal(str)

    def __init__(self, files, tab, delete, parent, test=False):
        """
        Keyword arguments:
        files  -- list with dicts containing file names
        tab -- instanseof AudioVideoTab, ImageTab or DocumentTab
               indicating currently active tab
        delete -- boolean that shows if files must removed after conversion
        parent -- parent widget

        files:
        Each dict have only one key and one corresponding value.
        Key is a file to be converted and it's value is the name of the new
        file that will be converted.

        Example list:
        [{"/foo/bar.png" : "/foo/bar.bmp"}, {"/f/bar2.png" : "/f/bar2.bmp"}]
        """
        super(Progress, self).__init__(parent)
        self.parent = parent

        self.files = files
        self.num_total_files = len(self.files)
        self.tab = tab
        self.delete = delete
        if not test:
            self._type = tab.name
            self.step = int(100 / len(files))
        self.ok = 0
        self.error = 0
        self.running = True

        self.nowQL = QLabel(self.tr('In progress: '))
        totalQL = QLabel(self.tr('Total:'))
        self.nowQPBar = QProgressBar()
        self.nowQPBar.setValue(0)
        self.totalQPBar = QProgressBar()
        self.totalQPBar.setValue(0)
        self.cancelQPB = QPushButton(self.tr('Cancel'))

        detailsQPB = QCommandLinkButton(self.tr('Details'))
        detailsQPB.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        detailsQPB.setCheckable(True)
        detailsQPB.setMaximumWidth(113)
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.outputQTE = QTextEdit()
        self.outputQTE.setReadOnly(True)
        self.frame = QFrame()
        frame_layout = utils.add_to_layout('h', self.outputQTE)
        self.frame.setLayout(frame_layout)
        self.frame.hide()

        hlayout = utils.add_to_layout('h', None, self.nowQL, None)
        hlayout2 = utils.add_to_layout('h', None, totalQL, None)
        hlayout3 = utils.add_to_layout('h', detailsQPB, line)
        hlayout4 = utils.add_to_layout('h', self.frame)
        hlayout5 = utils.add_to_layout('h', None, self.cancelQPB)
        vlayout = utils.add_to_layout(
                'v', hlayout, self.nowQPBar, hlayout2, self.totalQPBar, None,
                hlayout3, hlayout4, hlayout5
                )
        self.setLayout(vlayout)

        detailsQPB.toggled.connect(self.resize_dialog)
        detailsQPB.toggled.connect(self.frame.setVisible)
        self.cancelQPB.clicked.connect(self.reject)
        self.file_converted_signal.connect(self.next_file)
        self.refr_bars_signal.connect(self.refresh_progress_bars)
        self.update_text_edit_signal.connect(self.update_text_edit)

        self.resize(484, 200)
        self.setWindowTitle('FF Multi Converter - ' + self.tr('Conversion'))

        if not test:
            self.get_data() # should be first and not in QTimer.singleShot()
            QTimer.singleShot(0, self.manage_conversions)

    def get_data(self):
        """Collect conversion data from parents' widgets."""
        if self._type == 'AudioVideo':
            self.cmd = self.tab.commandQLE.text()
        elif self._type == 'Images':
            width = self.tab.widthQLE.text()
            self.size = ''
            self.mntaspect = False
            if width:
                height = self.tab.heightQLE.text()
                self.size = '{0}x{1}'.format(width, height)
                self.mntaspect = self.tab.imgaspectQChB.isChecked()
            self.imgcmd = self.tab.commandQLE.text()
            if self.tab.autocropQChB.isChecked():
                self.imgcmd += ' -trim +repage'
            rotate = self.tab.rotateQLE.text().strip()
            if rotate:
                self.imgcmd += ' -rotate {0}'.format(rotate)
            if self.tab.vflipQChB.isChecked():
                self.imgcmd += ' -flip'
            if self.tab.hflipQChB.isChecked():
                self.imgcmd += ' -flop'

    def resize_dialog(self):
        """Resize dialog."""
        height = 200 if self.frame.isVisible() else 366
        self.setMinimumSize(484, height)
        self.resize(484, height)

    def update_text_edit(self, txt):
        """Append txt to the end of current self.outputQTE's text."""
        current = self.outputQTE.toPlainText()
        self.outputQTE.setText(current+txt)
        self.outputQTE.moveCursor(QTextCursor.End)

    def refresh_progress_bars(self, now_percent):
        """Refresh the values of self.nowQPBar and self.totalQPBar."""
        total_percent = int(((now_percent * self.step) / 100) + self.min_value)

        if now_percent > self.nowQPBar.value() and not (now_percent > 100):
            self.nowQPBar.setValue(now_percent)
        if (total_percent > self.totalQPBar.value() and
        not (total_percent > self.max_value)):
            self.totalQPBar.setValue(total_percent)

    def manage_conversions(self):
        """
        Check whether all files have been converted.
        If not, it will allow convert_a_file() to convert the next file.
        """
        if not self.running:
            return
        if not self.files:
            self.totalQPBar.setValue(100)
        if self.totalQPBar.value() >= 100:
            sum_files = self.ok + self.error
            msg = QMessageBox(self)
            msg.setStandardButtons(QMessageBox.Ok)
            msg.setWindowTitle(self.tr("Report"))
            msg.setText(self.tr("Converted: {0}/{1}".format(self.ok,sum_files)))
            msg.setModal(False)
            msg.show()

            self.cancelQPB.setText(self.tr("Close"))
        else:
            self.convert_a_file()

    def next_file(self):
        """
        Update progress bars values, remove converted file from self.files
        and call manage_conversions() to continue the process.
        """
        self.totalQPBar.setValue(self.max_value)
        self.nowQPBar.setValue(100)
        QApplication.processEvents()
        self.files.pop(0)
        self.manage_conversions()

    def reject(self):
        """
        Use standard dialog to ask whether procedure must stop or not.
        Use the SIGSTOP to stop the conversion process while waiting for user
        to respond and SIGCONT or kill depending on user's answer.
        """
        if not self.files:
            QDialog.accept(self)
            return
        if self._type == 'AudioVideo':
            self.process.send_signal(signal.SIGSTOP)
        self.running = False
        reply = QMessageBox.question(
                self,
                'FF Multi Converter - ' + self.tr('Cancel Conversion'),
                self.tr('Are you sure you want to cancel conversion?'),
                QMessageBox.Yes|QMessageBox.Cancel
                )
        if reply == QMessageBox.Yes:
            if self._type == 'AudioVideo':
                self.process.kill()
            self.running = False
            self.thread.join()
            QDialog.reject(self)
        if reply == QMessageBox.Cancel:
            self.running = True
            if self._type == 'AudioVideo':
                self.process.send_signal(signal.SIGCONT)
            else:
                self.manage_conversions()

    def convert_a_file(self):
        """
        Update self.nowQL's text with current file's name, set self.nowQPBar
        value to zero and start the conversion procedure in a second thread
        using threading module.
        """
        if not self.files:
            return
        from_file = list(self.files[0].keys())[0]
        to_file = list(self.files[0].values())[0]

        text = os.path.basename(from_file[1:-1])
        num_file = self.num_total_files - len(self.files) + 1
        text += ' ({0}/{1})'.format(num_file, self.num_total_files)

        self.nowQL.setText(self.tr('In progress:') + ' ' + text)
        self.nowQPBar.setValue(0)

        self.min_value = self.totalQPBar.value()
        self.max_value = self.min_value + self.step

        if not os.path.exists(from_file[1:-1]):
            self.error += 1
            self.file_converted_signal.emit()
            return

        def convert():
            if self._type == 'AudioVideo':
                conv_func = self.convert_video
                params = (from_file, to_file, self.cmd)
            elif self._type == 'Images':
                conv_func = self.convert_image
                params = (from_file, to_file, self.size, self.mntaspect,
                          self.imgcmd)
            else:
                conv_func = self.convert_document
                params = (from_file, to_file)

            if conv_func(*params):
                self.ok += 1
                if self.delete and not from_file == to_file:
                    try:
                        os.remove(from_file[1:-1])
                    except OSError:
                        pass
            else:
                self.error += 1

            self.file_converted_signal.emit()

        self.thread = threading.Thread(target=convert)
        self.thread.start()

    def convert_video(self, from_file, to_file, command):
        """
        Create the ffmpeg command and execute it in a new process using the
        subprocess module. While the process is alive, parse ffmpeg output,
        estimate conversion progress using video's duration.
        With the result, emit the corresponding signal in order progressbars
        to be updated. Also emit regularly the corresponding signal in order
        an outputQTE to be updated with ffmpeg's output. Finally, save log
        information.

        Return True if conversion succeed, else False.
        """
        # note: from_file and to_file names are inside quotation marks
        convert_cmd = '{0} -y -i {1} {2} {3}'.format(
                self.parent.vidconverter, from_file, command, to_file)
        self.update_text_edit_signal.emit(convert_cmd + '\n')

        self.process = subprocess.Popen(
                shlex.split(convert_cmd),
                stderr=subprocess.STDOUT,
                stdout=subprocess.PIPE
                )

        final_output = myline = ''
        reader = io.TextIOWrapper(self.process.stdout, encoding='utf8')
        while True:
            out = reader.read(1)
            if out == '' and self.process.poll() is not None:
                break
            myline += out
            if out in ('\r', '\n'):
                m = re.search("Duration: ([0-9:.]+)", myline)
                if m:
                    total = utils.duration_in_seconds(m.group(1))
                n = re.search("time=([0-9:]+)", myline)
                # time can be of format 'time=hh:mm:ss.ts' or 'time=ss.ts'
                # depending on ffmpeg version
                if n:
                    time = n.group(1)
                    if ':' in time:
                        time = utils.duration_in_seconds(time)
                    now_sec = int(float(time))
                    try:
                        self.refr_bars_signal.emit(100 * now_sec / total)
                    except (UnboundLocalError, ZeroDivisionError):
                        pass
                self.update_text_edit_signal.emit(myline)
                final_output += myline
                myline = ''
        self.update_text_edit_signal.emit('\n\n')

        return_code = self.process.poll()

        log_data = {
                'command' : convert_cmd,
                'returncode' : return_code,
                'type' : 'VIDEO'
                }
        log_lvl = logging.info if return_code == 0 else logging.error
        log_lvl(final_output, extra=log_data)

        return return_code == 0

    def convert_image(self, from_file, to_file, size, mntaspect, imgcmd):
        """
        Convert an image using ImageMagick.
        Create conversion info ("cmd") and emit the corresponding signal
        in order an outputQTE to be updated with that info.
        Finally, save log information.

        Return True if conversion succeed, else False.
        """
        # note: from_file and to_file names are inside quotation marks
        resize = ''
        if size:
            resize = '-resize {0}'.format(size)
            if not mntaspect:
                resize += '\!'

        imgcmd = ' ' + imgcmd.strip() + ' '
        cmd = 'convert {0} {1}{2}{3}'.format(from_file, resize, imgcmd, to_file)
        self.update_text_edit_signal.emit(cmd + '\n')
        child = subprocess.Popen(
                shlex.split(cmd),
                stderr=subprocess.STDOUT,
                stdout=subprocess.PIPE
                )
        child.wait()

        reader = io.TextIOWrapper(child.stdout, encoding='utf8')
        final_output = reader.read()
        self.update_text_edit_signal.emit(final_output+'\n\n')

        return_code = child.poll()

        log_data = {
                'command' : cmd,
                'returncode' : return_code,
                'type' : 'IMAGE'
                }
        log_lvl = logging.info if return_code == 0 else logging.error
        log_lvl(final_output, extra=log_data)

        return return_code == 0

    def convert_document(self, from_file, to_file):
        """
        Create the unoconv command and execute it using the subprocess module.

        Emit the corresponding signal in order an outputQTE to be updated
        with unoconv's output. Finally, save log information.

        Return True if conversion succeed, else False.
        """
        # note: from_file and to_file names are inside quotation marks
        to_base, to_ext = os.path.splitext(to_file[1:-1])

        cmd = 'unoconv -f {0} -o {1} {2}'.format(to_ext[1:], to_file, from_file)
        self.update_text_edit_signal.emit(cmd + '\n')
        child = subprocess.Popen(
                shlex.split(cmd),
                stderr=subprocess.STDOUT,
                stdout=subprocess.PIPE
                )
        child.wait()

        reader = io.TextIOWrapper(child.stdout, encoding='utf8')
        final_output = reader.read()
        self.update_text_edit_signal.emit(final_output+'\n\n')

        return_code = child.poll()

        log_data = {
                'command' : cmd,
                'returncode' : return_code,
                'type' : 'DOCUMENT'
                }
        log_lvl = logging.info if return_code == 0 else logging.error
        log_lvl(final_output, extra=log_data)

        return return_code == 0
Exemplo n.º 11
0
 def hide(self):
   if self.do_hide:
     QFrame.hide(self)
Exemplo n.º 12
0
class AudioVideoTab(QWidget):
    def __init__(self, parent):
        super(AudioVideoTab, self).__init__(parent)
        self.parent = parent
        self.name = 'AudioVideo'
        self.formats = ['3gp', 'aac', 'ac3', 'afc', 'aiff', 'amr', 'asf', 'au',
                        'avi', 'dvd', 'flac', 'flv', 'mka', 'mkv', 'mmf',
                        'mov', 'mp3', 'mp4', 'mpg', 'ogg', 'ogv', 'psp', 'rm',
                        'spx', 'vob', 'wav', 'webm', 'wma', 'wmv']
        self.extra_formats = ['aifc', 'm2t', 'm4a', 'm4v', 'mp2', 'mpeg',
                              'ra', 'ts']

        nochange = self.tr('No Change')
        frequency_values = [nochange, '22050', '44100', '48000']
        bitrate_values = [nochange, '32', '96', '112', '128', '160', '192',
                          '256', '320']
        pattern = QRegExp(r'^[1-9]\d*')
        validator = QRegExpValidator(pattern, self)

        converttoLabel = QLabel(self.tr('Convert to:'))
        self.extComboBox = QComboBox()
        self.extComboBox.addItems(self.formats + [self.tr('Other')])
        self.extComboBox.setMinimumWidth(130)
        self.extLineEdit = QLineEdit()
        self.extLineEdit.setMaximumWidth(85)
        self.extLineEdit.setEnabled(False)
        hlayout1 = pyqttools.add_to_layout(QHBoxLayout(), converttoLabel,
                                           None, self.extComboBox,
                                           self.extLineEdit)
        commandLabel = QLabel(self.tr('Command:'))
        self.commandLineEdit = QLineEdit()
        self.presetButton = QPushButton(self.tr('Preset'))
        self.defaultButton = QPushButton(self.tr('Default'))
        hlayout2 = pyqttools.add_to_layout(QHBoxLayout(), commandLabel,
                                       self.commandLineEdit, self.presetButton,
                                       self.defaultButton)

        sizeLabel = QLabel(self.tr('Video Size:'))
        aspectLabel = QLabel(self.tr('Aspect:'))
        frameLabel = QLabel(self.tr('Frame Rate (fps):'))
        bitrateLabel = QLabel(self.tr('Video Bitrate (kbps):'))

        self.widthLineEdit = pyqttools.create_LineEdit((50, 16777215),
                                                       validator, 4)
        self.heightLineEdit = pyqttools.create_LineEdit((50, 16777215),
                                                        validator,4)
        label = QLabel('x')
        layout1 = pyqttools.add_to_layout(QHBoxLayout(), self.widthLineEdit,
                                          label, self.heightLineEdit)
        self.aspect1LineEdit = pyqttools.create_LineEdit((35, 16777215),
                                                         validator,2)
        self.aspect2LineEdit = pyqttools.create_LineEdit((35, 16777215),
                                                         validator,2)
        label = QLabel(':')
        layout2 = pyqttools.add_to_layout(QHBoxLayout(), self.aspect1LineEdit,
                                          label, self.aspect2LineEdit)
        self.frameLineEdit = pyqttools.create_LineEdit(None, validator, 4)
        self.bitrateLineEdit = pyqttools.create_LineEdit(None, validator, 6)

        labels = [sizeLabel, aspectLabel, frameLabel, bitrateLabel]
        widgets = [layout1, layout2, self.frameLineEdit, self.bitrateLineEdit]

        videosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            text = a.text()
            a.setText('<html><p align="center">{0}</p></html>'.format(text))
            layout = pyqttools.add_to_layout(QVBoxLayout(), a, b)
            videosettings_layout.addLayout(layout)

        freqLabel = QLabel(self.tr('Frequency (Hz):'))
        chanLabel = QLabel(self.tr('Channels:'))
        bitrateLabel = QLabel(self.tr('Audio Bitrate (kbps):'))

        self.freqComboBox = QComboBox()
        self.freqComboBox.addItems(frequency_values)
        self.chan1RadioButton = QRadioButton('1')
        self.chan1RadioButton.setMaximumSize(QSize(51, 16777215))
        self.chan2RadioButton = QRadioButton('2')
        self.chan2RadioButton.setMaximumSize(QSize(51, 16777215))
        self.group = QButtonGroup()
        self.group.addButton(self.chan1RadioButton)
        self.group.addButton(self.chan2RadioButton)
        spcr1 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        spcr2 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        chanlayout = pyqttools.add_to_layout(QHBoxLayout(), spcr1,
                                             self.chan1RadioButton,
                                             self.chan2RadioButton, spcr2)
        self.audio_bitrateComboBox = QComboBox()
        self.audio_bitrateComboBox.addItems(bitrate_values)

        labels = [freqLabel, chanLabel, bitrateLabel]
        widgets = [self.freqComboBox, chanlayout, self.audio_bitrateComboBox]

        audiosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            text = a.text()
            a.setText('<html><p align="center">{0}</p></html>'.format(text))
            layout = pyqttools.add_to_layout(QVBoxLayout(), a, b)
            audiosettings_layout.addLayout(layout)

        hidden_layout = pyqttools.add_to_layout(QVBoxLayout(),
                                                videosettings_layout,
                                                audiosettings_layout)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.moreButton = QPushButton(QApplication.translate('Tab', 'More'))
        self.moreButton.setSizePolicy(QSizePolicy(QSizePolicy.Fixed))
        self.moreButton.setCheckable(True)
        hlayout3 = pyqttools.add_to_layout(QHBoxLayout(), line, self.moreButton)

        self.frame = QFrame()
        self.frame.setLayout(hidden_layout)
        self.frame.hide()

        final_layout = pyqttools.add_to_layout(QVBoxLayout(), hlayout1,
                                               hlayout2, hlayout3, self.frame)
        self.setLayout(final_layout)


        self.presetButton.clicked.connect(self.choose_preset)
        self.defaultButton.clicked.connect(self.set_default_command)
        self.moreButton.toggled.connect(self.frame.setVisible)
        self.moreButton.toggled.connect(self.resize_parent)
        self.extComboBox.currentIndexChanged.connect(
                lambda: self.extLineEdit.setEnabled(
                self.extComboBox.currentIndex() == len(self.formats)))
        self.widthLineEdit.textChanged.connect(
                lambda: self.command_elements_change('size'))
        self.heightLineEdit.textChanged.connect(
                lambda: self.command_elements_change('size'))
        self.aspect1LineEdit.textChanged.connect(
                lambda: self.command_elements_change('aspect'))
        self.aspect2LineEdit.textChanged.connect(
                lambda: self.command_elements_change('aspect'))
        self.frameLineEdit.textChanged.connect(
                lambda: self.command_elements_change('frames'))
        self.bitrateLineEdit.textChanged.connect(
                lambda: self.command_elements_change('video_bitrate'))
        self.freqComboBox.currentIndexChanged.connect(
                lambda: self.command_elements_change('frequency'))
        self.audio_bitrateComboBox.currentIndexChanged.connect(
                lambda: self.command_elements_change('audio_bitrate'))
        self.chan1RadioButton.clicked.connect(
                lambda: self.command_elements_change('channels1'))
        self.chan2RadioButton.clicked.connect(
                lambda: self.command_elements_change('channels2'))

    def resize_parent(self):
        """Resize MainWindow."""
        height = MAIN_FIXED_HEIGHT if self.frame.isVisible() else MAIN_HEIGHT
        self.parent.setMinimumSize(MAIN_WIDTH, height)
        self.parent.resize(MAIN_WIDTH, height)

    def clear(self):
        """Clear all values of graphical widgets."""
        lines = [self.commandLineEdit, self.widthLineEdit, self.heightLineEdit,
                 self.aspect1LineEdit, self.aspect2LineEdit, self.frameLineEdit,
                 self.bitrateLineEdit, self.extLineEdit]
        for i in lines:
            i.clear()

        self.freqComboBox.setCurrentIndex(0)
        self.audio_bitrateComboBox.setCurrentIndex(0)
        self.group.setExclusive(False)
        self.chan1RadioButton.setChecked(False)
        self.chan2RadioButton.setChecked(False)
        self.group.setExclusive(True)
        # setExclusive(False) in order to be able to uncheck checkboxes and
        # then setExclusive(True) so only one radio button can be set

    def ok_to_continue(self):
        """
        Check if everything is ok with audiovideotab to continue conversion.

        Check if:
        - Either ffmpeg or avconv are installed.
        - Desired extension is valid.
        - self.commandLineEdit is empty.

        Return True if all tests pass, else False.
        """
        if not self.parent.ffmpeg and not self.parent.avconv:
            QMessageBox.warning(self, 'FF Multi Converter - ' + self.tr(
                'Error!'), self.tr('Neither ffmpeg nor avconv are installed.'
                '\nYou will not be able to convert audio/video files until you'
                ' install one of them.'))
            return False
        if self.extLineEdit.isEnabled():
            text = str(self.extLineEdit.text()).strip()
            if len(text.split()) != 1 or text[0] == '.':
                QMessageBox.warning(self, 'FF Multi Converter - ' + self.tr(
                    'Error!'), self.tr('Extension must be one word and must '
                    'not start with a dot.'))
                self.extLineEdit.selectAll()
                self.extLineEdit.setFocus()
                return False
        if not self.commandLineEdit.text():
            QMessageBox.warning(self, 'FF Multi Converter - ' + self.tr(
                  'Error!'), self.tr('The command LineEdit may not be empty.'))
            self.commandLineEdit.setFocus()
            return False
        return True

    def set_default_command(self):
        """Set the default value to self.commandLineEdit."""
        self.clear()
        self.commandLineEdit.setText(self.parent.default_command)

    def choose_preset(self):
        """
        Open the presets dialog and update self.commandLineEdit,
        self.extComboBox and self.extLineEdit with the appropriate values.
        """
        dialog = presets_dlgs.ShowPresets()
        if dialog.exec_() and dialog.the_command is not None:
            self.commandLineEdit.setText(dialog.the_command)
            self.commandLineEdit.home(False)
            find = self.extComboBox.findText(dialog.the_extension)
            if find >= 0:
                self.extComboBox.setCurrentIndex(find)
            else:
                self.extComboBox.setCurrentIndex(len(self.formats))
                self.extLineEdit.setText(dialog.the_extension)

    def remove_consecutive_spaces(self, string):
        """Remove any consecutive spaces from a string and return it."""
        temp = string
        string = ''
        for i in temp.split():
            if i:
                string += i + ' '
        return string[:-1]

    def command_elements_change(self, widget):
        """Fill self.commandLineEdit with the appropriate command parameters."""
        command = str(self.commandLineEdit.text())

        if widget == 'size':
            text1 = self.widthLineEdit.text()
            text2 = self.heightLineEdit.text()

            if (text1 or text2) and not (text1 and text2):
                return
            f = re.sub(r'^.*(-s\s+\d+x\d+).*$', r'\1', command)
            if re.match(r'^.*(-s\s+\d+x\d+).*$', f):
                command = command.replace(f, '').strip()
            if text1 and text2:
                command += ' -s {0}x{1}'.format(text1, text2)

        elif widget == 'aspect':
            text1 = self.aspect1LineEdit.text()
            text2 = self.aspect2LineEdit.text()

            if (text1 or text2) and not (text1 and text2):
                return
            f = re.sub(r'^.*(-aspect\s+\d+:\d+).*$', r'\1', command)
            if re.match(r'^.*(-aspect\s+\d+:\d+).*$', f):
                command = command.replace(f, '').strip()
            if text1 and text2:
                command += ' -aspect {0}:{1}'.format(text1, text2)

        elif widget == 'frames':
            text = self.frameLineEdit.text()
            f = re.sub(r'^.*(-r\s+\d+).*$', r'\1', command)
            if re.match(r'^.*(-r\s+\d+).*$', f):
                command = command.replace(f, '').strip()
            if text:
                command += ' -r {0}'.format(text)

        elif widget == 'video_bitrate':
            text = self.bitrateLineEdit.text()
            f = re.sub(r'^.*(-b\s+\d+k).*$', r'\1', command)
            if re.match(r'^.*(-b\s+\d+k).*$', f):
                command = command.replace(f, '')
            if text:
                command += ' -b {0}k'.format(text)
            command = command.replace('-sameq', '').strip()

        elif widget == 'frequency':
            text = self.freqComboBox.currentText()
            f = re.sub(r'^.*(-ar\s+\d+).*$', r'\1', command)
            if re.match(r'^.*(-ar\s+\d+).*$', f):
                command = command.replace(f, '').strip()
            if text != 'No Change':
                command += ' -ar {0}'.format(text)

        elif widget == 'audio_bitrate':
            text = self.audio_bitrateComboBox.currentText()
            f = re.sub(r'^.*(-ab\s+\d+k).*$', r'\1', command)
            if re.match(r'^.*(-ab\s+\d+k).*$', f):
                command = command.replace(f, '').strip()
            if text != 'No Change':
                command += ' -ab {0}k'.format(text)

        elif widget in ('channels1', 'channels2'):
            text = self.chan1RadioButton.text() if widget == 'channels1' \
                                            else self.chan2RadioButton.text()
            f = re.sub(r'^.*(-ac\s+\d+).*$', r'\1', command)
            if re.match(r'^.*(-ac\s+\d+).*$', f):
                command = command.replace(f, '').strip()
            command += ' -ac {0}'.format(text)

        self.commandLineEdit.clear()
        self.commandLineEdit.setText(self.remove_consecutive_spaces(command))
Exemplo n.º 13
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)
        self.__env = _asmappingproxy(kwargs.get("env", {}))

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self.__reportData = None

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground,
                                          self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-red.png"))

        if not self.resizing_enabled:
            self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    def __restoreWidgetGeometry(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored and not self.windowState() & \
                    (Qt.WindowMaximized | Qt.WindowFullScreen):
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored and self.isVisible():
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before first showEvent and we must not overwrite the the
        # savedGeometry with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position and not self.__was_restored:
            # Restore saved geometry on show
            self.__restoreWidgetGeometry()
            self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError('{} is not a valid output signal for widget {}'.format(
                signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    def onDeleteWidget(self):
        """
        Invoked by the canvas to notify the widget it has been deleted
        from the workflow.

        If possible, subclasses should gracefully cancel any currently
        executing tasks.
        """
        pass

    def handleNewSignals(self):
        """
        Invoked by the workflow signal propagation manager after all
        signals handlers have been called.

        Reimplement this method in order to coalesce updates from
        multiple updated inputs.
        """
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        iconpaths = {
            "Info": gui.resource_filename("icons/information.png"),
            "Warning": gui.resource_filename("icons/warning.png"),
            "Error": gui.resource_filename("icons/error.png")
        }
        items = []

        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconpaths[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            info = QPixmap(gui.resource_filename("icons/information.png"))
            warning = QPixmap(gui.resource_filename("icons/warning.png"))
            error = QPixmap(gui.resource_filename("icons/error.png"))
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """
        Set blocking flag for this widget.

        While this flag is set this widget and all its descendants
        will not receive any new signals from the workflow signal manager.

        This is useful for instance if the widget does it's work in a
        separate thread or schedules processing from the event queue.
        In this case it can set the blocking flag in it's processNewSignals
        method schedule the task and return immediately. After the task
        has completed the widget can clear the flag and send the updated
        outputs.

        .. note::
            Failure to clear this flag will block dependent nodes forever.
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """
        Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)

    def workflowEnv(self):
        """
        Return (a view to) the workflow runtime environment.

        Returns
        -------
        env : types.MappingProxyType
        """
        return self.__env

    def workflowEnvChanged(self, key, value, oldvalue):
        """
        A workflow environment variable `key` has changed to value.

        Called by the canvas framework to notify widget of a change
        in the workflow runtime environment.

        The default implementation does nothing.
        """
        pass
Exemplo n.º 14
0
class RipperWidget(SmoothWidget, ripper.Ripper):
    """ One-button multi-track ripper.
    """
    def __init__(self, parent=None):
        SmoothWidget.__init__(self, parent)
        ripper.Ripper.__init__(self)
        Layout = QVBoxLayout(self)

        self.discLabel = QLabel(self)
        Layout.addWidget(self.discLabel)

        # ONLY ONE BUTTON!
        self.button = QPushButton('Cancel', self)
        self.button.setEnabled(True)
        self.button.hide()
        QObject.connect(self.button, SIGNAL('clicked()'),
                        self.cancel)
        Layout.addWidget(self.button)

        self.catFrame = QFrame(self)
        self.catFrame.setFrameStyle(QFrame.StyledPanel)
        self.catFrame.hide()
        QHBoxLayout(self.catFrame)
        self.categories = CategoryButtons(self.catFrame)
        QObject.connect(self.categories, SIGNAL('selected(QString &)'),
                        self.start)
        self.catFrame.layout().addWidget(self.categories)
        Layout.addWidget(self.catFrame)
        
        self.progress = QProgressBar(self)
        self.progress.setRange(0, 100)
        self.progress.hide()
        Layout.addWidget(self.progress)

    def start(self, category):
        """ Should already have the track info. """
        self.genre = str(category)
        self._progress_data = {}
        self.disc_length = 0.0
        for i in range(self.count):
            sec = cd_logic.get_track_time_total(i)
            self._progress_data[i] = {'rip' : 0,
                                      'enc' : 0,
                                      'length' : sec}
            self.disc_length += sec
        self.rip_n_encode()

        text = '%s - %s' % (self.artist, self.album)
        self.discLabel.setText('Copying "'+text+'"...')
        self.button.show()
        self.progress.show()
        self.catFrame.hide()
            
    def cancel(self):        
        self.stop_request = True
        path = os.path.join(ripper.LIBRARY,
                            self.genre,
                            self.artist+' - '+self.album)
        os.system('rm -rf \"%s\"' % path)
        self.discLabel.setText('Canceled')
        self.button.hide()
        self.progress.hide()
        self.progress.setValue(0)
        self.emit(SIGNAL('canceled()'))

    def read_disc_info(self):
        self.discLabel.setText('Getting disc info...')
        ripper.Ripper.read_disc_info(self)

    def ripper_event(self, e):
        """ """
        event = QEvent(event_type(e.type))
        event.data = e.__dict__
        QApplication.instance().postEvent(self, event)

    def customEvent(self, e):
        if e.type() == event_type(ripper.CDDB_DONE):
            self.button.setEnabled(True)
            text = '%s - %s' % (self.artist, self.album)
            text += '           ( Select a category... )'
            self.discLabel.setText(text)
            self.catFrame.show()
            self.emit(SIGNAL('foundDiscInfo()'))
        elif e.type() == event_type(ripper.STATUS_UPDATE):
            self.updateProgress(e.data)
            self.emit(SIGNAL('status(QEvent *)'), e)

    def updateProgress(self, data):
        """
        assume rip and enc are equal in time.
        """
        state = data['state']
        tracknum = data['tracknum']
        percent = data['percent']
        goal_percents = self.count * 200

        item = self._progress_data[tracknum]
        if state == 'rip':
            item['rip'] = percent
        elif state == 'enc':
            item['enc'] = percent
        else:
            # err, etc...
            item['rip'] = 0
            item['enc'] = 0

        total = 0
        for i, v in self._progress_data.items():
            seconds = v['length']
            rip_perc = v['rip']
            enc_perc = v['enc']

            worth = seconds / self.disc_length
            rip_perc *= worth
            enc_perc *= worth
            total += rip_perc + enc_perc
            #print i, worth, rip_perc, enc_perc, total
        percent = total / 2
        self.progress.setValue(percent)

        if percent == 100:
            if self.is_ripping:
                print 'percent == 100 but still ripping?'
            if self.is_encoding:
                print 'percent == 100 but still encoding?'
            else:
                print 'percent == 100 and ripper finished'
            self.button.hide()
            self.progress.hide()
            text = 'Finished copying \"%s - %s\"' % (self.artist, self.album)
            self.discLabel.setText(text)
            self.emit(SIGNAL('doneRipping()'))
class AudioVideoTab(QWidget):
    def __init__(self, parent):
        super(AudioVideoTab, self).__init__(parent)
        self.parent = parent
        self.name = 'AudioVideo'

        self.defaultStr = self.tr('Default')
        self.DisableStream = self.tr('Disable')

        self.formats = config.video_formats
        frequency_values = [self.defaultStr] + config.video_frequency_values
        bitrate_values = [self.defaultStr] + config.video_bitrate_values

        rotation_options = [
            self.tr('None'), '90 ' + self.tr('clockwise'),
            '90 ' + self.tr('clockwise') + ' + ' + self.tr('vertical flip'),
            '90 ' + self.tr('counter clockwise'), '90 ' +
            self.tr('counter clockwise') + ' + ' + self.tr('vertical flip'),
            '180',
            self.tr('horizontal flip'),
            self.tr('vertical flip')
        ]

        digits_validator = QRegExpValidator(QRegExp(r'[1-9]\d*'), self)
        digits_validator_wzero = QRegExpValidator(QRegExp(r'\d*'), self)
        digits_validator_minus = QRegExpValidator(QRegExp(r'(-1|[1-9]\d*)'),
                                                  self)
        time_validator = QRegExpValidator(
            QRegExp(r'\d{1,2}:\d{1,2}:\d{1,2}\.\d+'), self)

        converttoQL = QLabel(self.tr('Convert to:'))
        self.extQCB = QComboBox()
        self.extQCB.setMinimumWidth(100)
        vidcodecQL = QLabel('Video codec:')
        self.vidcodecQCB = QComboBox()
        self.vidcodecQCB.setMinimumWidth(110)
        audcodecQL = QLabel('Audio codec:')
        self.audcodecQCB = QComboBox()
        self.audcodecQCB.setMinimumWidth(110)

        hlayout1 = utils.add_to_layout('h', converttoQL, self.extQCB,
                                       QSpacerItem(180, 20), vidcodecQL,
                                       self.vidcodecQCB, audcodecQL,
                                       self.audcodecQCB)

        commandQL = QLabel(self.tr('Command:'))
        self.commandQLE = QLineEdit()
        self.presetQPB = QPushButton(self.tr('Preset'))
        self.defaultQPB = QPushButton(self.defaultStr)
        hlayout2 = utils.add_to_layout('h', commandQL, self.commandQLE,
                                       self.presetQPB, self.defaultQPB)

        sizeQL = QLabel(self.tr('Video Size:'))
        aspectQL = QLabel(self.tr('Aspect:'))
        frameQL = QLabel(self.tr('Frame Rate (fps):'))
        bitrateQL = QLabel(self.tr('Video Bitrate (kbps):'))

        self.widthQLE = utils.create_LineEdit((70, 16777215),
                                              digits_validator_minus, 4)
        self.heightQLE = utils.create_LineEdit((70, 16777215),
                                               digits_validator_minus, 4)
        label = QLabel('<html><p align="center">x</p></html>')
        layout1 = utils.add_to_layout('h', self.widthQLE, label,
                                      self.heightQLE)
        self.aspect1QLE = utils.create_LineEdit((50, 16777215),
                                                digits_validator, 2)
        self.aspect2QLE = utils.create_LineEdit((50, 16777215),
                                                digits_validator, 2)
        label = QLabel('<html><p align="center">:</p></html>')
        layout2 = utils.add_to_layout('h', self.aspect1QLE, label,
                                      self.aspect2QLE)
        self.frameQLE = utils.create_LineEdit((120, 16777215),
                                              digits_validator, 4)
        self.bitrateQLE = utils.create_LineEdit((130, 16777215),
                                                digits_validator, 6)

        labels = [sizeQL, aspectQL, frameQL, bitrateQL]
        widgets = [layout1, layout2, self.frameQLE, self.bitrateQLE]

        self.preserveaspectQChB = QCheckBox(self.tr("Preserve aspect ratio"))
        self.preservesizeQChB = QCheckBox(self.tr("Preserve video size"))

        preserve_layout = utils.add_to_layout('v', self.preserveaspectQChB,
                                              self.preservesizeQChB)

        videosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            a.setText('<html><p align="center">{0}</p></html>'.format(
                a.text()))
            layout = utils.add_to_layout('v', a, b)
            videosettings_layout.addLayout(layout)
            if a == aspectQL:
                # add vidaspectCB in layout after aspectQL
                videosettings_layout.addLayout(preserve_layout)

        freqQL = QLabel(self.tr('Frequency (Hz):'))
        chanQL = QLabel(self.tr('Audio Channels:'))
        bitrateQL = QLabel(self.tr('Audio Bitrate (kbps):'))
        threadsQL = QLabel(self.tr('Threads:'))

        self.freqQCB = QComboBox()
        self.freqQCB.addItems(frequency_values)
        self.chan1QRB = QRadioButton('1')
        self.chan1QRB.setMaximumSize(QSize(51, 16777215))
        self.chan2QRB = QRadioButton('2')
        self.chan2QRB.setMaximumSize(QSize(51, 16777215))
        self.group = QButtonGroup()
        self.group.addButton(self.chan1QRB)
        self.group.addButton(self.chan2QRB)
        spcr1 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        spcr2 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        chanlayout = utils.add_to_layout('h', spcr1, self.chan1QRB,
                                         self.chan2QRB, spcr2)
        self.audbitrateQCB = QComboBox()
        self.audbitrateQCB.addItems(bitrate_values)
        self.threadsQLE = utils.create_LineEdit((50, 16777215),
                                                digits_validator_wzero, 1)

        labels = [freqQL, bitrateQL, chanQL, threadsQL]
        widgets = [
            self.freqQCB, self.audbitrateQCB, chanlayout, self.threadsQLE
        ]

        audiosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            a.setText('<html><p align="center">{0}</p></html>'.format(
                a.text()))
            layout = utils.add_to_layout('v', a, b)
            audiosettings_layout.addLayout(layout)

        time_format = " (hh:mm:ss):"
        beginQL = QLabel(self.tr("Split file. Begin time") + time_format)
        self.beginQLE = utils.create_LineEdit(None, time_validator, None)
        durationQL = QLabel(self.tr("Duration") + time_format)
        self.durationQLE = utils.create_LineEdit(None, time_validator, None)

        hlayout4 = utils.add_to_layout('h', beginQL, self.beginQLE, durationQL,
                                       self.durationQLE)

        embedQL = QLabel(self.tr("Embed subtitle:"))
        self.embedQLE = QLineEdit()
        self.embedQTB = QToolButton()
        self.embedQTB.setText("...")

        rotateQL = QLabel(self.tr("Rotate:"))
        self.rotateQCB = QComboBox()
        self.rotateQCB.addItems(rotation_options)

        hlayout5 = utils.add_to_layout('h', rotateQL, self.rotateQCB, embedQL,
                                       self.embedQLE, self.embedQTB)

        hidden_layout = utils.add_to_layout('v', videosettings_layout,
                                            audiosettings_layout, hlayout4,
                                            hlayout5)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.moreQPB = QPushButton(QApplication.translate('Tab', 'More'))
        self.moreQPB.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.moreQPB.setCheckable(True)
        hlayout3 = utils.add_to_layout('h', line, self.moreQPB)

        self.frame = QFrame()
        self.frame.setLayout(hidden_layout)
        self.frame.hide()

        final_layout = utils.add_to_layout('v', hlayout1, hlayout2, hlayout3,
                                           self.frame)
        self.setLayout(final_layout)

        self.presetQPB.clicked.connect(self.choose_preset)
        self.defaultQPB.clicked.connect(self.set_default_command)
        self.embedQTB.clicked.connect(self.open_subtitle_file)
        self.moreQPB.toggled.connect(self.frame.setVisible)
        self.moreQPB.toggled.connect(
            lambda: QTimer.singleShot(100, self.resize_parent))
        self.widthQLE.textChanged.connect(self.command_update_size)
        self.heightQLE.textChanged.connect(self.command_update_size)
        self.aspect1QLE.textChanged.connect(self.command_update_aspect)
        self.aspect2QLE.textChanged.connect(self.command_update_aspect)
        self.frameQLE.textChanged.connect(self.command_update_frames)
        self.bitrateQLE.textChanged.connect(self.command_update_vidbitrate)
        self.threadsQLE.textChanged.connect(self.command_update_threads)
        self.beginQLE.textChanged.connect(self.command_update_begin_time)
        self.durationQLE.textChanged.connect(self.command_update_duration)
        self.embedQLE.textChanged.connect(self.command_update_subtitles)
        self.vidcodecQCB.currentIndexChanged.connect(
            self.command_update_vcodec)
        self.audcodecQCB.currentIndexChanged.connect(
            self.command_update_acodec)
        self.freqQCB.currentIndexChanged.connect(self.command_update_frequency)
        self.rotateQCB.currentIndexChanged.connect(
            self.command_update_rotation)
        self.audbitrateQCB.currentIndexChanged.connect(
            self.command_update_audbitrate)
        self.chan1QRB.clicked.connect(
            lambda: self.command_update_channels('1'))
        self.chan2QRB.clicked.connect(
            lambda: self.command_update_channels('2'))
        self.preserveaspectQChB.toggled.connect(
            self.command_update_preserve_aspect)
        self.preservesizeQChB.toggled.connect(
            self.command_update_preserve_size)

    def resize_parent(self):
        """Give MainWindow its default size."""
        self.parent.setMinimumSize(self.parent.sizeHint())
        self.parent.resize(self.parent.sizeHint())

    def clear(self):
        """Clear all values of graphical widgets."""
        lines = [
            self.commandQLE, self.widthQLE, self.heightQLE, self.aspect1QLE,
            self.aspect2QLE, self.frameQLE, self.bitrateQLE, self.threadsQLE,
            self.beginQLE, self.embedQLE, self.durationQLE
        ]
        for i in lines:
            i.clear()

        self.vidcodecQCB.setCurrentIndex(0)
        self.audcodecQCB.setCurrentIndex(0)
        self.freqQCB.setCurrentIndex(0)
        self.audbitrateQCB.setCurrentIndex(0)
        self.rotateQCB.setCurrentIndex(0)
        self.preserveaspectQChB.setChecked(False)
        self.preservesizeQChB.setChecked(False)
        self.group.setExclusive(False)
        self.chan1QRB.setChecked(False)
        self.chan2QRB.setChecked(False)
        self.group.setExclusive(True)
        # setExclusive(False) in order to be able to uncheck checkboxes and
        # then setExclusive(True) so only one radio button can be set

    def fill_video_comboboxes(self, vcodecs, acodecs, extraformats):
        vcodecs = [i for i in vcodecs.split("\n")] if vcodecs else []
        acodecs = [i for i in acodecs.split("\n")] if acodecs else []
        extraformats = [i for i in extraformats.split("\n")
                        ] if extraformats else []

        self.vidcodecQCB.currentIndexChanged.disconnect()
        self.audcodecQCB.currentIndexChanged.disconnect()

        self.vidcodecQCB.clear()
        self.audcodecQCB.clear()
        self.extQCB.clear()
        self.vidcodecQCB.addItems([self.defaultStr, self.DisableStream] +
                                  vcodecs)
        self.audcodecQCB.addItems([self.defaultStr, self.DisableStream] +
                                  acodecs)
        self.extQCB.addItems(sorted(self.formats + extraformats))

        self.vidcodecQCB.currentIndexChanged.connect(
            self.command_update_vcodec)
        self.audcodecQCB.currentIndexChanged.connect(
            self.command_update_acodec)

    def ok_to_continue(self):
        """
        Check if everything is ok with audiovideotab to continue conversion.

        Check if:
        - Either ffmpeg or avconv are installed.

        Return True if all tests pass, else False.
        """
        if self.parent.vidconverter is None:
            QMessageBox.warning(
                self, 'FF Multi Converter - ' + self.tr('Error!'),
                self.
                tr('Neither ffmpeg nor libav are installed.'
                   '\nYou will not be able to convert audio/video files until you'
                   ' install one of them.'))
            return False
        return True

    def open_subtitle_file(self):
        """
        Get the filename using standard QtDialog and update embedQLE's text.
        """
        fname = QFileDialog.getOpenFileName(
            self, 'FF Multi Converter - ' + self.tr('Choose File'),
            config.home, 'Subtitles (*.srt *.sub *.ssa *.ass)')
        if fname:
            self.embedQLE.setText(fname)

    def set_default_command(self):
        """Set the default value to self.commandQLE."""
        self.clear()
        self.commandQLE.setText(self.parent.default_command)

    def choose_preset(self):
        """
        Open the presets dialog and update self.commandQLE,
        and self.extQCB and with the appropriate values.
        """
        dialog = presets_dlgs.ShowPresets(choose=True)
        if dialog.exec_() and dialog.the_command is not None:
            self.clear()
            self.commandQLE.setText(dialog.the_command)
            self.commandQLE.home(False)
            find = self.extQCB.findText(dialog.the_extension)
            if find >= 0:
                self.extQCB.setCurrentIndex(find)

    def command_update_size(self):
        command = self.commandQLE.text()
        text1 = self.widthQLE.text()
        text2 = self.heightQLE.text()

        if not (text1 == '-1' or text2 == '-1'):
            self.preserveaspectQChB.setChecked(False)

        if (text1 or text2) and not (text1 and text2) or (text1 == '-'
                                                          or text2 == '-'):
            return

        regex = r'(\s+|^)-s(:v){0,1}\s+\d+x\d+(\s+|$)'
        if re.search(regex, command):
            command = re.sub(regex, '', command)

        regex = r'(,*\s*){0,1}(scale=-?\d+:-?\d+)(\s*,*\s*){0,1}'
        _filter = "scale={0}:{1}".format(text1,
                                         text2) if text1 and text2 else ''

        self.commandQLE.setText(
            utils.update_cmdline_text(command, _filter, regex,
                                      bool(text1 and text2), 0, 2))

    def command_update_preserve_size(self):
        checked = self.preservesizeQChB.isChecked()

        self.widthQLE.setEnabled(not checked)
        self.heightQLE.setEnabled(not checked)

        if checked:
            self.widthQLE.clear()
            self.heightQLE.clear()
            # command_update_size() is triggered here

        command = self.commandQLE.text()
        regex = r'(\s+|^)-s\s+\d+x\d+(\s+|$)'
        command = re.sub(' +', ' ', re.sub(regex, ' ', command)).strip()
        self.commandQLE.setText(command)

    def command_update_aspect(self):
        command = self.commandQLE.text()
        text1 = self.aspect1QLE.text()
        text2 = self.aspect2QLE.text()

        if (text1 or text2) and not (text1 and text2):
            return

        regex = r'(\s+|^)-aspect\s+\d+:\d+(\s+|$)'
        s = ' -aspect {0}:{1} '.format(text1,
                                       text2) if text1 and text2 else ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_preserve_aspect(self):
        command = self.commandQLE.text()
        checked = self.preserveaspectQChB.isChecked()

        self.aspect1QLE.setEnabled(not checked)
        self.aspect2QLE.setEnabled(not checked)

        if checked:
            self.aspect1QLE.clear()
            self.aspect2QLE.clear()
            # self.command_update_aspect() is triggered here

            regex = r'(,*\s*){0,1}(scale=(-?\d+):(-?\d+))(\s*,*\s*){0,1}'
            search = re.search(regex, command)
            if search:
                width = search.groups()[2]
                height = search.groups()[3]
                if not (width == '-1' or height == '-1'):
                    s = "scale=-1:{0}".format(height)
                    command = re.sub(regex, r'\1{0}\5'.format(s), command)
                    self.widthQLE.setText('-1')
                    self.heightQLE.setText(height)

        regex = r'(\s+|^)-aspect\s+\d+:\d+(\s+|$)'
        command = re.sub(' +', ' ', re.sub(regex, ' ', command)).strip()
        self.commandQLE.setText(command)

    def command_update_frames(self):
        command = self.commandQLE.text()
        text = self.frameQLE.text()

        regex = r'(\s+|^)-r\s+\d+(\s+|$)'
        s = ' -r {0} '.format(text) if text else ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_vidbitrate(self):
        command = self.commandQLE.text()
        text = self.bitrateQLE.text()

        regex = r'(\s+|^)-b(:v){0,1}\s+\d+[kKmM](\s+|$)'
        s = ' -b:v {0}k '.format(text) if text else ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub('-sameq', '', command)
        command = re.sub(' +', ' ', command).strip()

        self.commandQLE.setText(command)

    def command_update_frequency(self):
        command = self.commandQLE.text()
        text = self.freqQCB.currentText()

        regex = r'(\s+|^)-ar\s+\d+(\s+|$)'
        s = ' -ar {0} '.format(
            text) if self.freqQCB.currentIndex() != 0 else ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_audbitrate(self):
        command = self.commandQLE.text()
        text = self.audbitrateQCB.currentText()

        regex = r'(\s+|^)-(ab|b:a)\s+\d+[kKmM](\s+|$)'
        if self.audbitrateQCB.currentIndex() != 0:
            s = ' -b:a {0}k '.format(text)
        else:
            s = ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_channels(self, channel):
        command = self.commandQLE.text()

        regex = r'(\s+|^)-ac\s+\d+(\s+|$)'
        s = ' -ac {0} '.format(channel)

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_threads(self):
        command = self.commandQLE.text()
        text = self.threadsQLE.text()

        regex = r'(\s+|^)-threads\s+\d+(\s+|$)'
        s = ' -threads {0} '.format(text) if text else ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_begin_time(self):
        command = self.commandQLE.text()
        text = self.beginQLE.text()

        regex = r'(\s+|^)-ss\s+\S+(\s+|$)'
        s = ' -ss {0} '.format(text) if text else ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_duration(self):
        command = self.commandQLE.text()
        text = self.durationQLE.text()

        regex = r'(\s+|^)-t\s+\S+(\s+|$)'
        s = ' -t {0} '.format(text) if text else ' '

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_vcodec(self):
        command = self.commandQLE.text()
        text = self.vidcodecQCB.currentText()

        regex = r'(\s+|^)-(vcodec|c:v)\s+\S+(\s+|$)'
        regex_vn = r'(\s+|^)-vn(\s+|$)'
        if self.vidcodecQCB.currentIndex() == 1:
            s = ' -vn '.format(text)
        elif self.vidcodecQCB.currentIndex() == 0:
            s = ' '
        else:
            s = ' -vcodec {0} '.format(text)

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        elif re.search(regex_vn, command):
            command = re.sub(regex_vn, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_acodec(self):
        command = self.commandQLE.text()
        text = self.audcodecQCB.currentText()

        regex = r'(\s+|^)-(acodec|c:a)\s+\S+(\s+|$)'
        regex_an = r'(\s+|^)-an(\s+|$)'
        if self.audcodecQCB.currentIndex() == 1:
            s = ' -an '.format(text)
        elif self.audcodecQCB.currentIndex() == 0:
            s = ' '
        else:
            s = ' -acodec {0} '.format(text)

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        elif re.search(regex_an, command):
            command = re.sub(regex_an, s, command)
        else:
            command += s

        command = re.sub(' +', ' ', command).strip()
        self.commandQLE.setText(command)

    def command_update_subtitles(self):
        command = self.commandQLE.text()
        regex = r'(,*\s*){0,1}(subtitles=\'.*\')(\s*,*\s*){0,1}'

        text = self.embedQLE.text()
        _filter = "subtitles='{0}'".format(text) if text else ''

        self.commandQLE.setText(
            utils.update_cmdline_text(command, _filter, regex, bool(text), 0,
                                      2))

    def command_update_rotation(self):
        command = self.commandQLE.text()
        regex = r'(,*\s*){0,1}(transpose=\d(,\s*transpose=\d)*|vflip|hflip)(\s*,*\s*){0,1}'

        rotate = self.rotateQCB.currentIndex()
        if rotate == 0:  # none
            _filter = ''
        elif rotate == 1:  # 90 clockwise
            _filter = 'transpose=1'
        elif rotate == 2:  # 90 clockwise + vertical flip
            _filter = 'transpose=3'
        elif rotate == 3:  # 90 counter clockwise
            _filter = 'transpose=2'
        elif rotate == 4:  # 90 counter clockwise + vertical flip
            _filter = 'transpose=0'
        elif rotate == 5:  # 180
            _filter = 'transpose=2,transpose=2'
        elif rotate == 6:  # horizontal flip
            _filter = 'hflip'
        elif rotate == 7:  # vertical flip
            _filter = 'vflip'

        self.commandQLE.setText(
            utils.update_cmdline_text(command, _filter, regex,
                                      bool(rotate != 0), 0, 3))
Exemplo n.º 16
0
class OWWidget(QDialog, Report, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget Meta Description
    # -----------------------

    #: Widget name (:class:`str`) as presented in the Canvas
    name = None
    id = None
    category = None
    version = None
    #: Short widget description (:class:`str` optional), displayed in
    #: canvas help tooltips.
    description = None
    #: A longer widget description (:class:`str` optional)
    long_description = None
    #: Widget icon path relative to the defining module
    icon = "icons/Unknown.png"
    #: Widget priority used for sorting within a category
    #: (default ``sys.maxsize``).
    priority = sys.maxsize

    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None

    #: A list of published input definitions
    inputs = []
    #: A list of published output definitions
    outputs = []

    # Default widget GUI layout settings
    # ----------------------------------

    #: Should the widget have basic layout
    #: (If this flag is false then the `want_main_area` and
    #: `want_control_area` are ignored).
    want_basic_layout = True
    #: Should the widget construct a `mainArea` (this is a resizable
    #: area to the right of the `controlArea`).
    want_main_area = True
    #: Should the widget construct a `controlArea`.
    want_control_area = True
    #: Widget painted by `Save graph" button
    graph_name = None
    graph_writers = FileFormat.img_writers
    want_status_bar = False

    save_position = True

    #: If false the widget will receive fixed size constraint
    #: (derived from it's layout). Use for widgets which have simple
    #: static size contents.
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    #: A list of advice messages (:class:`Message`) to display to the user.
    #: When a widget is first shown a message from this list is selected
    #: for display. If a user accepts (clicks 'Ok. Got it') the choice is
    #: recorded and the message is never shown again (closing the message
    #: will not mark it as seen). Messages can be displayed again by pressing
    #: Shift + F1
    #:
    #: :type: list of :class:`Message`
    UserAdviceMessages = []

    def __new__(cls, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)
        self.__env = _asmappingproxy(kwargs.get("env", {}))

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self.__reportData = None

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False

        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        self.__msgwidget = None
        self.__msgchoice = 0

        if self.want_basic_layout:
            self.insertLayout()

        sc = QShortcut(QKeySequence(Qt.ShiftModifier | Qt.Key_F1), self)
        sc.activated.connect(self.__quicktip)

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    def inline_graph_report(self):
        box = gui.widgetBox(self.controlArea, orientation="horizontal")
        box.layout().addWidget(self.graphButton)
        box.layout().addWidget(self.report_button)
#        self.report_button_background.hide()
#        self.graphButtonBackground.hide()

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    class Splitter(QSplitter):
        def createHandle(self):
            return self.Handle(self.orientation(), self,
                                   cursor=Qt.PointingHandCursor)
        class Handle(QSplitterHandle):
            def mouseReleaseEvent(self, event):
                if event.button() == Qt.LeftButton:
                    splitter = self.splitter()
                    splitter.setSizes([int(splitter.sizes()[0] == 0), 1000])
                super().mouseReleaseEvent(event)
            def mouseMoveEvent(self, event):
                return  # Prevent moving; just show/hide

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.want_main_area = self.graph_name is not None or self.want_main_area

        splitter = self.Splitter(Qt.Horizontal, self)
        self.layout().addWidget(splitter)

        if self.want_control_area:
            self.controlArea = gui.widgetBox(splitter,
                                             orientation="vertical",
                                             margin=0)
            splitter.setSizes([1])  # Results in smallest size allowed by policy

            if self.graph_name is not None or hasattr(self, "send_report"):
                leftSide = self.controlArea
                self.controlArea = gui.widgetBox(leftSide, margin=0)
            if self.graph_name is not None:
                self.graphButton = gui.button(leftSide, None, "&Save Graph")
                self.graphButton.clicked.connect(self.save_graph)
                self.graphButton.setAutoDefault(0)
            if hasattr(self, "send_report"):
                self.report_button = gui.button(leftSide, None, "&Report",
                                                callback=self.show_report)
                self.report_button.setAutoDefault(0)

            if self.want_main_area:
                self.controlArea.setSizePolicy(QSizePolicy.Fixed,
                                               QSizePolicy.MinimumExpanding)
            self.controlArea.layout().setContentsMargins(4, 4, 0 if self.want_main_area else 4, 4)
        if self.want_main_area:
            self.mainArea = gui.widgetBox(splitter,
                                          orientation="vertical",
                                          margin=4,
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding))
            splitter.setCollapsible(1, False)
            self.mainArea.layout().setContentsMargins(0 if self.want_control_area else 4, 4, 4, 4)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-red.png"))

        if not self.resizing_enabled:
            self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)

    def save_graph(self):
        graph_obj = getdeepattr(self, self.graph_name, None)
        if graph_obj is None:
            return
        saveplot.save_plot(graph_obj, self.graph_writers)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    def __restoreWidgetGeometry(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored and not self.windowState() & \
                    (Qt.WindowMaximized | Qt.WindowFullScreen):
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored and self.isVisible():
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before first showEvent and we must not overwrite the the
        # savedGeometry with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position and not self.__was_restored:
            # Restore saved geometry on show
            self.__restoreWidgetGeometry()
            self.__was_restored = True
        self.__quicktipOnce()

    def wheelEvent(self, event):
        # Silently accept the wheel event. This is to ensure combo boxes
        # and other controls that have focus don't receive this event unless
        # the cursor is over them.
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        """
        Send a `value` on the `signalName` widget output.

        An output with `signalName` must be defined in the class ``outputs``
        list.
        """
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError('{} is not a valid output signal for widget {}'.format(
                signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    def onDeleteWidget(self):
        """
        Invoked by the canvas to notify the widget it has been deleted
        from the workflow.

        If possible, subclasses should gracefully cancel any currently
        executing tasks.
        """
        pass

    def handleNewSignals(self):
        """
        Invoked by the workflow signal propagation manager after all
        signals handlers have been called.

        Reimplement this method in order to coalesce updates from
        multiple updated inputs.
        """
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    @contextlib.contextmanager
    def progressBar(self, iterations=0):
        """
        Context manager for progress bar.

        Using it ensures that the progress bar is removed at the end without
        needing the `finally` blocks.

        Usage:

            with self.progressBar(20) as progress:
                ...
                progress.advance()

        or

            with self.progressBar() as progress:
                ...
                progress.advance(0.15)

        or

            with self.progressBar():
                ...
                self.progressBarSet(50)

        :param iterations: the number of iterations (optional)
        :type iterations: int
        """
        progress_bar = gui.ProgressBar(self, iterations)
        yield progress_bar
        progress_bar.finish()  # Let us not rely on garbage collector

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        """
        Set widget's status message.

        This is a short status string to be displayed inline next to
        the instantiated widget icon in the canvas.
        """
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        """
        Return the widget's status message.
        """
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        """
        Set/clear a widget information message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        """
        Set/clear a widget warning message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        """
        Set/clear a widget error message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        iconpaths = {
            "Info": gui.resource_filename("icons/information.png"),
            "Warning": gui.resource_filename("icons/warning.png"),
            "Error": gui.resource_filename("icons/error.png")
        }
        items = []

        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconpaths[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            info = QPixmap(gui.resource_filename("icons/information.png"))
            warning = QPixmap(gui.resource_filename("icons/warning.png"))
            error = QPixmap(gui.resource_filename("icons/error.png"))
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """
        Set blocking flag for this widget.

        While this flag is set this widget and all its descendants
        will not receive any new signals from the workflow signal manager.

        This is useful for instance if the widget does it's work in a
        separate thread or schedules processing from the event queue.
        In this case it can set the blocking flag in it's processNewSignals
        method schedule the task and return immediately. After the task
        has completed the widget can clear the flag and send the updated
        outputs.

        .. note::
            Failure to clear this flag will block dependent nodes forever.
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """
        Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)

    def workflowEnv(self):
        """
        Return (a view to) the workflow runtime environment.

        Returns
        -------
        env : types.MappingProxyType
        """
        return self.__env

    def workflowEnvChanged(self, key, value, oldvalue):
        """
        A workflow environment variable `key` has changed to value.

        Called by the canvas framework to notify widget of a change
        in the workflow runtime environment.

        The default implementation does nothing.
        """
        pass

    def __showMessage(self, message):
        if self.__msgwidget is not None:
            self.__msgwidget.hide()
            self.__msgwidget.deleteLater()
            self.__msgwidget = None

        if message is None:
            return

        buttons = MessageOverlayWidget.Ok | MessageOverlayWidget.Close
        if message.moreurl is not None:
            buttons |= MessageOverlayWidget.Help

        if message.icon is not None:
            icon = message.icon
        else:
            icon = Message.Information

        self.__msgwidget = MessageOverlayWidget(
            parent=self, text=message.text, icon=icon, wordWrap=True,
            standardButtons=buttons)

        b = self.__msgwidget.button(MessageOverlayWidget.Ok)
        b.setText("Ok, got it")

        self.__msgwidget.setStyleSheet("""
            MessageOverlayWidget {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop:0 #666, stop:0.3 #6D6D6D, stop:1 #666)
            }
            MessageOverlayWidget QLabel#text-label {
                color: white;
            }"""
        )

        if message.moreurl is not None:
            helpbutton = self.__msgwidget.button(MessageOverlayWidget.Help)
            helpbutton.setText("Learn more\N{HORIZONTAL ELLIPSIS}")
            self.__msgwidget.helpRequested.connect(
                lambda: QDesktopServices.openUrl(QUrl(message.moreurl)))

        self.__msgwidget.setWidget(self)
        self.__msgwidget.show()

    def __quicktip(self):
        messages = list(self.UserAdviceMessages)
        if messages:
            message = messages[self.__msgchoice % len(messages)]
            self.__msgchoice += 1
            self.__showMessage(message)

    def __quicktipOnce(self):
        filename = os.path.join(settings.widget_settings_dir(),
                               "user-session-state.ini")
        namespace = ("user-message-history/{0.__module__}.{0.__qualname__}"
                     .format(type(self)))
        session_hist = QSettings(filename, QSettings.IniFormat)
        session_hist.beginGroup(namespace)
        messages = self.UserAdviceMessages

        def ispending(msg):
            return not session_hist.value(
                "{}/confirmed".format(msg.persistent_id),
                defaultValue=False, type=bool)
        messages = list(filter(ispending, messages))

        if not messages:
            return

        message = messages[self.__msgchoice % len(messages)]
        self.__msgchoice += 1

        self.__showMessage(message)

        def userconfirmed():
            session_hist = QSettings(filename, QSettings.IniFormat)
            session_hist.beginGroup(namespace)
            session_hist.setValue(
                "{}/confirmed".format(message.persistent_id), True)
            session_hist.sync()

        self.__msgwidget.accepted.connect(userconfirmed)
Exemplo n.º 17
0
class Progress(QDialog):
    file_converted_signal = pyqtSignal()
    refr_bars_signal = pyqtSignal(int)
    update_text_edit_signal = pyqtSignal(str)

    def __init__(self, files, _type, cmd, ffmpeg, size, mntaspect, delete,
                 parent, test=False):
        """
        Keyword arguments:
        files  -- list with dicts containing file names
        _type  -- 'AudioVideo', 'Images' or 'Documents' depending files type
        cmd    -- ffmpeg command, for audio/video conversions
        ffmpeg -- if True ffmpeg will be used, else avconv
                  for audio/video conversions
        size   -- new image size string of type 'widthxheight' eg. '300x300'
                  for image conversions
        mntaspect -- boolean indicating whether aspect ratio must be maintained
                     for image conversions
        delete -- boolean that shows if files must removed after conversion

        files:
        Each dict have only one key and one corresponding value.
        Key is a file to be converted and it's value is the name of the new
        file that will be converted.

        Example list:
        [{"/foo/bar.png" : "/foo/bar.bmp"}, {"/f/bar2.png" : "/f/bar2.bmp"}]
        """
        super(Progress, self).__init__(parent)
        self.parent = parent
        self._type = _type
        self.cmd = cmd
        self.ffmpeg = ffmpeg
        self.size = size
        self.mntaspect = mntaspect

        self.files = files
        self.delete = delete
        if not test:
            self.step = int(100 / len(files))
        self.ok = 0
        self.error = 0
        self.running = True

        self.nowLabel = QLabel(self.tr('In progress: '))
        totalLabel = QLabel(self.tr('Total:'))
        self.nowBar = QProgressBar()
        self.nowBar.setValue(0)
        self.totalBar = QProgressBar()
        self.totalBar.setValue(0)
        self.cancelButton = QPushButton(self.tr('Cancel'))

        detailsButton = QCommandLinkButton(self.tr('Details'))
        detailsButton.setSizePolicy(QSizePolicy(QSizePolicy.Fixed))
        detailsButton.setCheckable(True)
        detailsButton.setMaximumWidth(113)
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.textEdit = QTextEdit()
        self.textEdit.setReadOnly(True)
        self.frame = QFrame()
        frame_layout = pyqttools.add_to_layout(QHBoxLayout(), self.textEdit)
        self.frame.setLayout(frame_layout)
        self.frame.hide()

        hlayout = pyqttools.add_to_layout(QHBoxLayout(), None, self.nowLabel,
                                          None)
        hlayout2 = pyqttools.add_to_layout(QHBoxLayout(), None, totalLabel,
                                           None)
        hlayout3 = pyqttools.add_to_layout(QHBoxLayout(), detailsButton, line)
        hlayout4 = pyqttools.add_to_layout(QHBoxLayout(), self.frame)
        hlayout5 = pyqttools.add_to_layout(QHBoxLayout(), None,
                                           self.cancelButton)
        vlayout = pyqttools.add_to_layout(QVBoxLayout(), hlayout, self.nowBar,
                                          hlayout2, self.totalBar, None,
                                          hlayout3, hlayout4, hlayout5)
        self.setLayout(vlayout)

        detailsButton.toggled.connect(self.resize_dialog)
        detailsButton.toggled.connect(self.frame.setVisible)
        self.cancelButton.clicked.connect(self.reject)
        self.file_converted_signal.connect(self.file_converted)
        self.refr_bars_signal.connect(self.refresh_progress_bars)
        self.update_text_edit_signal.connect(self.update_text_edit)

        self.resize(484, 200)
        self.setWindowTitle('FF Multi Converter - ' + self.tr('Conversion'))

        if not test:
            QTimer.singleShot(0, self.manage_conversions)

    def resize_dialog(self):
        """Resize dialog."""
        height = 200 if self.frame.isVisible() else 366
        self.setMinimumSize(484, height)
        self.resize(484, height)

    def update_text_edit(self, txt):
        """Append txt to the end of current self.textEdit's text."""
        current = self.textEdit.toPlainText()
        self.textEdit.setText(current+txt)
        self.textEdit.moveCursor(QTextCursor.End)

    def refresh_progress_bars(self, now_percent):
        """Refresh the values of self.nowBar and self.totalBar."""
        total_percent = int(((now_percent * self.step) / 100) + self.min_value)

        if now_percent > self.nowBar.value() and not (now_percent > 100):
            self.nowBar.setValue(now_percent)
        if (total_percent > self.totalBar.value() and
        not (total_percent > self.max_value)):
            self.totalBar.setValue(total_percent)

    def manage_conversions(self):
        """
        Check whether all files have been converted.
        If not, it will allow convert_a_file() to convert the next file.
        """
        if not self.running:
            return
        if not self.files:
            self.totalBar.setValue(100)
        if self.totalBar.value() >= 100:
            sum_files = self.ok + self.error
            msg = QMessageBox(self)
            msg.setStandardButtons(QMessageBox.Ok)
            msg.setWindowTitle(self.tr("Report"))
            msg.setText(self.tr("Converted: %1/%2").arg(self.ok).arg(sum_files))
            msg.setModal(False)
            msg.show()

            self.cancelButton.setText(self.tr("Close"))
            if self._type == 'Documents':
                self.parent.docconv = False  # doc conversion end
        else:
            self.convert_a_file()

    def file_converted(self):
        """
        Update progress bars values, remove converted file from self.files
        and call manage_conversions() to continue the process.
        """
        self.totalBar.setValue(self.max_value)
        self.nowBar.setValue(100)
        QApplication.processEvents()
        self.files.pop(0)
        self.manage_conversions()

    def reject(self):
        """
        Use standard dialog to ask whether procedure must stop or not.
        Use the SIGSTOP to stop the conversion process while waiting for user
        to respond and SIGCONT or kill depending on user's answer.
        """
        if not self.files:
            QDialog.accept(self)
            return
        if self._type == 'AudioVideo':
            self.process.send_signal(signal.SIGSTOP)
        self.running = False
        reply = QMessageBox.question(self,
            'FF Multi Converter - ' + self.tr('Cancel Conversion'),
            self.tr('Are you sure you want to cancel conversion?'),
            QMessageBox.Yes|QMessageBox.Cancel)
        if reply == QMessageBox.Yes:
            if self._type == 'AudioVideo':
                self.process.kill()
            if self._type == 'Documents':
                self.parent.docconv = False
            self.running = False
            self.thread.join()
            QDialog.reject(self)
        if reply == QMessageBox.Cancel:
            self.running = True
            if self._type == 'AudioVideo':
                self.process.send_signal(signal.SIGCONT)
            else:
                self.manage_conversions()

    def convert_a_file(self):
        """
        Update self.nowLabel's text with current file's name, set self.nowBar
        value to zero and start the conversion procedure in a second thread
        using threading module.
        """
        if not self.files:
            return
        from_file = self.files[0].keys()[0]
        to_file = self.files[0].values()[0]

        if len(from_file) > 40:
            # split file name if it is too long in order to display it properly
            text = '.../' + from_file.split('/')[-1]
        else:
            text = from_file

        self.nowLabel.setText(self.tr('In progress:') + ' ' + text)
        self.nowBar.setValue(0)

        self.min_value = self.totalBar.value()
        self.max_value = self.min_value + self.step

        if not os.path.exists(from_file[1:-1]):
            self.error += 1
            self.file_converted_signal.emit()
            return

        def convert():
            if self._type == 'AudioVideo':
                conv_func = self.convert_video
                params = (from_file, to_file, self.cmd, self.ffmpeg)
            elif self._type == 'Images':
                conv_func = self.convert_image
                params = (from_file, to_file, self.size, self.mntaspect)
            else:
                conv_func = self.convert_doc
                params = (from_file, to_file)

            if conv_func(*params):
                self.ok += 1
                if self.delete:
                    try:
                        os.remove(from_file[1:-1])
                    except OSError:
                        pass
            else:
                self.error += 1

            self.file_converted_signal.emit()

        self.thread = threading.Thread(target=convert)
        self.thread.start()

    def duration_in_seconds(self, duration):
        """
        Return the number of seconds of duration, an integer.
        Duration is a strinf of type hh:mm:ss.ts
        """
        duration = duration.split('.')[0]
        hours, mins, secs = duration.split(':')
        seconds = int(secs)
        seconds += (int(hours) * 3600) + (int(mins) * 60)
        return seconds

    def convert_video(self, from_file, to_file, command, ffmpeg):
        """
        Create the ffmpeg command and execute it in a new process using the
        subprocess module. While the process is alive, parse ffmpeg output,
        estimate conversion progress using video's duration.
        With the result, emit the corresponding signal in order progressbars
        to be updated. Also emit regularly the corresponding signal in order
        an textEdit to be updated with ffmpeg's output. Finally, save log
        information.

        Return True if conversion succeed, else False.
        """
        assert isinstance(from_file, unicode) and isinstance(to_file, unicode)
        assert from_file.startswith('"') and from_file.endswith('"')
        assert to_file.startswith('"') and to_file.endswith('"')

        converter = 'ffmpeg' if ffmpeg else 'avconv'
        convert_cmd = '{0} -y -i {1} {2} {3}'.format(converter, from_file,
                                                     command, to_file)
        convert_cmd = str(QString(convert_cmd).toUtf8())
        self.update_text_edit_signal.emit(unicode(convert_cmd, 'utf-8')+'\n')

        self.process = subprocess.Popen(shlex.split(convert_cmd),
                                        stderr=subprocess.STDOUT,
                                        stdout=subprocess.PIPE)

        final_output = myline = str('')
        while True:
            out = str(QString(self.process.stdout.read(1)).toUtf8())
            if out == str('') and self.process.poll() is not None:
                break

            myline += out
            if out in (str('\r'), str('\n')):
                m = re.search("Duration: ([0-9:.]+)", myline)
                if m:
                    total = self.duration_in_seconds(m.group(1))
                n = re.search("time=([0-9:]+)", myline)
                # time can be of format 'time=hh:mm:ss.ts' or 'time=ss.ts'
                # depending on ffmpeg version
                if n:
                    time = n.group(1)
                    if ':' in time:
                        time = self.duration_in_seconds(time)
                    now_sec = int(float(time))
                    try:
                        self.refr_bars_signal.emit(100 * now_sec / total)
                    except UnboundLocalError, ZeroDivisionError:
                        pass
                self.update_text_edit_signal.emit(myline)
                final_output += myline
                myline = str('')
        self.update_text_edit_signal.emit('\n\n')

        return_code = self.process.poll()

        log_data = {'command' : unicode(convert_cmd, 'utf-8'),
                    'returncode' : return_code, 'type' : 'VIDEO'}
        log_lvl = logging.info if return_code == 0 else logging.error
        log_lvl(unicode(final_output, 'utf-8'), extra=log_data)

        return return_code == 0
Exemplo n.º 18
0
class Window(QMainWindow):
    def __init__(self,parent = None):
        QMainWindow.__init__(self,parent)
        self.resize(1024,768)
        self.setWindowTitle("Sabel")
        self.setWindowIcon(Icons.sabel)
        self.centralwidget = QWidget(self)
        self.horizontalLayout = QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setMargin(0)
        self.cmdList = config.cmds()
        self.paramList = config.params()
        
        '''A.Editor TabWidget'''
        '''This parent is for findbar and vertical layout'''
        self.editorLayoutWidget = QWidget(self)
        self.editorLayoutWidget.setMinimumWidth(800)
        self.tabWidget = EditorTab(self)
        self.editorLayout = QVBoxLayout(self.editorLayoutWidget)
        self.editorLayout.setMargin(0)
        self.editorLayout.addWidget(self.tabWidget)
        
        "0.Style Layout"
        self.styleLayoutWidget = QFrame()
        self.styleLayoutWidget.setFrameShape(QFrame.StyledPanel)
        self.styleLayout = QHBoxLayout(self.styleLayoutWidget)
        self.styleTest = QPushButton(self.styleLayoutWidget)
        self.styleTest.setText("Change Styles")
        self.styleTest.clicked.connect(self.changeStyleSheet)
        self.popWidget = Popup(self.styleLayoutWidget)
        self.styleLayout.addWidget(self.styleTest)
        self.styleLayout.addWidget(self.popWidget)
        self.styleLayout.setMargin(0)
        self.editorLayout.addWidget(self.styleLayoutWidget)
        self.styleLayoutWidget.hide()
        
        "1.Find Layout"
        self.findLayoutWidget = QFrame()
        self.findLayoutWidget.setFrameShape(QFrame.StyledPanel)
        self.findLayout = QHBoxLayout(self.findLayoutWidget)
        self.lineEdit = QLineEdit(self.findLayoutWidget)
        self.lineEdit_2 = QLineEdit(self.findLayoutWidget)
        self.findClose = QPushButton(self.findLayoutWidget)
        self.findClose.setIcon(Icons.close_view)
        self.findClose.setFlat(True)
        self.findClose.clicked.connect(self.findBarShow)
        self.find = QPushButton(self.findLayoutWidget)
        self.find.setText("Find")
        self.find.clicked.connect(self.findCurrentText)
        self.replacefind = QPushButton(self.findLayoutWidget)
        self.replacefind.setText("Replace/Find")
        self.replacefind.clicked.connect(self.replaceFindText)
        self.replace = QPushButton(self.findLayoutWidget)
        self.replace.setText("Replace")
        self.replace.clicked.connect(self.replaceCurrentText)
        self.replaceAll = QPushButton(self.findLayoutWidget)
        self.replaceAll.setText("Replace All")
        self.replaceAll.clicked.connect(self.replaceAllText)
        self.caseSensitive = QToolButton(self.findLayoutWidget)
        self.caseSensitive.setIcon(Icons.font)
        self.caseSensitive.setCheckable(True)
        self.wholeWord = QToolButton(self.findLayoutWidget)
        self.wholeWord.setText("ww")
        self.wholeWord.setCheckable(True)
        self.regex = QToolButton(self.findLayoutWidget)
        self.regex.setText("re")
        self.regex.setCheckable(True)
        self.backward = QToolButton(self.findLayoutWidget)
        self.backward.setText("bk")
        self.backward.setCheckable(True)
        self.backward.setDisabled(True)
        self.findLayout.addWidget(self.findClose)
        self.findLayout.addWidget(self.find)
        self.findLayout.addWidget(self.lineEdit)
        self.findLayout.addWidget(self.lineEdit_2)
        self.findLayout.addWidget(self.caseSensitive)
        self.findLayout.addWidget(self.wholeWord)
        self.findLayout.addWidget(self.regex)
        self.findLayout.addWidget(self.backward)
        self.findLayout.addWidget(self.replacefind)
        self.findLayout.addWidget(self.replace)
        self.findLayout.addWidget(self.replaceAll)
        self.findLayout.setMargin(0)
        self.findLayoutWidget.setMaximumHeight(25)
        self.editorLayout.addWidget(self.findLayoutWidget)
        self.findLayoutWidget.hide()
        
        
        '''B.Designer'''
        '''This parent is for widgetsbar and design layout'''
        self.designerLayoutWidget = QWidget(self)
        self.designerLayoutWidget.setMinimumWidth(800)
        self.designerWidget = Screen(self)
        self.designerLayoutWidget.hide()
        self.designerLayout = QVBoxLayout(self.designerLayoutWidget)
        self.designerLayout.setMargin(0)
        self.designerLayout.addWidget(self.designerWidget)
        
        '''C.Level Editor'''
        '''This parent is for spritesheets and level layout'''
        self.levelLayoutWidget = QWidget(self)
        self.levelLayoutWidget.setMinimumWidth(800)
        self.levelWidget = Level(self)
        self.levelLayoutWidget.hide()
        self.levelLayout = QVBoxLayout(self.levelLayoutWidget)
        self.levelLayout.setMargin(0)
        self.levelLayout.addWidget(self.levelWidget)
        
        '''D.Explorer TabWidget'''
        self.explorerTabWidget = TreeTab(self)
        #self.explorerTabWidget.setMaximumWidth(200)
        '''1.Project Tree'''
        self.tab_5 = QWidget()
        #self.tab_5.setMaximumWidth(200)
        self.VerticalLayout_2 = QVBoxLayout(self.tab_5)#QHBoxLayout(self.tab_5)
        self.VerticalLayout_2.setMargin(0)
        self.treeWidget = ProjectTree(self.tab_5)
        #self.treeWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        #self.treeWidget.horizontalScrollBar().show()
        self.VerticalLayout_2.addWidget(self.treeWidget)
        
        '''2.Outline Tree'''
        self.tab_2 = QWidget()
        #self.tab_2.setMaximumWidth(200)
        self.VerticalLayout_3 = QVBoxLayout(self.tab_2)
        self.VerticalLayout_3.setMargin(0)
        self.outlineWidget = OutlineTree(self.tab_2)
        self.outlineWidget.itemDoubleClicked.connect(self.gotoLine)
        self.VerticalLayout_3.addWidget(self.outlineWidget)
        
        '''E.Output TabWidget'''
        self.outputTabWidget = OutputTab(self)
        self.tabWidget.currentChanged.connect(self.fileChanged)
        self.explorerTabWidget.currentChanged.connect(self.closeExplorer)
        self.outputTabWidget.currentChanged.connect(self.closeConsole)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.setTabShape(0)
            
        '''1.Output layout'''
        #must check
        self.tab_6 = QWidget()
        self.horizontalLayout_2 = QVBoxLayout(self.tab_6)
        self.horizontalLayout_2.setMargin(0)
        self.textEdit = QTextEdit()
        self.inputLayout = QHBoxLayout()
        self.inputLayout.setMargin(0)
        self.fileButton = QPushButton()
        self.fileButton.setText("File")
        self.fileButton.clicked.connect(self.getFile)
        self.runButton = QPushButton()
        self.runButton.setFlat(True)
        self.runButton.setIcon(Icons.go)
        self.combo = QComboBox()
        self.combo.setFixedWidth(100)
        self.comboAdd = QPushButton()
        self.comboAdd.setIcon(Icons.add)
        self.comboAdd.setFlat(True)
        self.comboAdd.clicked.connect(self.addCmd)
        self.comboDel = QPushButton()
        self.comboDel.setIcon(Icons.close_view)
        self.comboDel.setFlat(True)
        self.comboDel.clicked.connect(self.delCmd)
        self.combo2 = QComboBox()
        self.combo2.setFixedWidth(500)
        self.combo2Add = QPushButton()
        self.combo2Add.setIcon(Icons.add)
        self.combo2Add.setFlat(True)
        self.combo2Add.clicked.connect(self.addParam)
        self.combo2Del = QPushButton()
        self.combo2Del.setIcon(Icons.close_view)
        self.combo2Del.setFlat(True)
        self.combo2Del.clicked.connect(self.delParam)
        if(self.checkHasValue(self.cmdList)):
            for cmd in self.cmdList:
                self.combo.addItem(cmd)
        else:
            self.cmdList = []
        if(self.checkHasValue(self.paramList)):
            for param in self.paramList:
                self.combo2.addItem(param)
        else:
            self.paramList = []
        
        self.horizontalLayout_2.addWidget(self.textEdit)
        self.inputLayout.addWidget(QLabel("<b>Command:</b>"))
        self.inputLayout.addWidget(self.combo)
        self.inputLayout.addWidget(self.comboAdd)
        self.inputLayout.addWidget(self.comboDel)
        self.inputLayout.addWidget(QLabel("<b>Parameters:</b>"))
        self.inputLayout.addWidget(self.combo2)
        self.inputLayout.addWidget(self.combo2Add)
        self.inputLayout.addWidget(self.combo2Del)
        self.inputLayout.addWidget(self.fileButton)
        self.inputLayout.addWidget(self.runButton)
        self.horizontalLayout_2.addLayout(self.inputLayout)
        
        '''2.Error Layout'''
        self.tab_7 = QWidget()
        self.horizontalLayout_4 = QHBoxLayout(self.tab_7)
        self.horizontalLayout_4.setMargin(0)
        self.errorTree = ErrorTree(self.tab_7)
        self.errorTree.itemDoubleClicked.connect(self.errorLine)
        self.horizontalLayout_4.addWidget(self.errorTree)
        
        '''TabWidgets tabs'''
        #self.designerWidget.addTab(QWidget(self),"")
        #self.designerWidget.setTabIcon(0,Icons.close_view)
        #self.levelWidget.addTab(QWidget(self),"")
        #self.levelWidget.setTabIcon(0,Icons.close_view)
        
        self.explorerTabWidget.addTab(self.tab_5,"Projects")
        self.explorerTabWidget.addTab(self.tab_2,"Outline")
        self.explorerTabWidget.addTab(QWidget(self),"")
        self.explorerTabWidget.setTabIcon(0,Icons.cprj)
        self.explorerTabWidget.setTabIcon(1,Icons.envvar)
        self.explorerTabWidget.setTabIcon(2,Icons.close_view)
        self.outputTabWidget.addTab(self.tab_7,"Error")
        self.outputTabWidget.addTab(self.tab_6,"Output")
        self.outputTabWidget.addTab(QWidget(self),"")
        self.outputTabWidget.setTabIcon(0,Icons.error)
        self.outputTabWidget.setTabIcon(1,Icons.console_view)
        self.outputTabWidget.setTabIcon(2,Icons.close_view)
        
        '''Splitters'''
        self.split1 = QSplitter(Qt.Horizontal)
        self.split1.addWidget(self.explorerTabWidget)
        self.split1.addWidget(self.editorLayoutWidget)
        self.split1.addWidget(self.designerLayoutWidget)
        self.split1.addWidget(self.levelLayoutWidget)
        #self.split1.addWidget(self.tab_5)
        
        self.split2 = QSplitter(Qt.Vertical)
        self.split2.addWidget(self.split1)
        self.split2.addWidget(self.outputTabWidget)
        self.horizontalLayout.addWidget(self.split2)
        
        
        '''Status Bar'''
        self.statusbar = QStatusBar(self)
        self.aboutButton = QPushButton(self)
        self.aboutButton.setFlat(True)
        self.aboutButton.setIcon(Icons.anchor)
        self.aboutButton.clicked.connect(self.about)
        self.expButton = QPushButton(self)
        self.expButton.setFlat(True)
        self.expButton.setIcon(Icons.prj)
        self.expButton.clicked.connect(self.exp)
        self.cmdButton = QPushButton(self)
        self.cmdButton.setFlat(True)
        self.cmdButton.setIcon(Icons.console_view)
        self.cmdButton.clicked.connect(self.cmd)
        self.cmdButton.setShortcut('Ctrl+D')
        self.imgButton = QPushButton(self)
        self.imgButton.setFlat(True)
        self.imgButton.setIcon(Icons.color_palette)
        self.imgButton.clicked.connect(self.design)
        self.imgButton.setShortcut('Ctrl+I')
        self.findButton = QPushButton(self)
        self.findButton.setFlat(True)
        self.findButton.setIcon(Icons.find)
        self.findButton.setShortcut("Ctrl+F")
        self.findButton.clicked.connect(self.findBarShow)
        '''
        self.zoominButton = QPushButton(self)
        self.zoominButton.setFlat(True)
        self.zoominButton.setIcon(Icons.zoomplus)
        self.zoominButton.clicked.connect(self.zoomin)
        self.zoomoutButton = QPushButton(self)
        self.zoomoutButton.setFlat(True)
        self.zoomoutButton.setIcon(Icons.zoomminus)
        self.zoomoutButton.clicked.connect(self.zoomout)
        '''

        '''Status Text,Line Text, Progress Bar and Stop Button'''
        self.statusText = QLabel("Writable")
        #self.statusText.setAlignment(Qt.AlignCenter)
        self.statusText.setFixedWidth(200)
        self.lineText = QLabel("")
        self.lineText.setFixedWidth(50)
        
        self.progressbar = QProgressBar()
        self.progressbar.setMinimum(0)
        self.progressbar.setMaximum(100)
        self.stopButton = QPushButton(self)
        self.stopButton.setFlat(True)
        self.stopButton.setIcon(Icons.stop)
        self.stopButton.clicked.connect(self.forceStop)
        self.progressbar.hide()
        self.stopButton.hide()
        self.temp = False
        self.progress = False
        self.counter = 0
        
        '''Adding all widgets to Status Bar'''
        self.statusbar.addWidget(self.aboutButton)
        self.statusbar.addWidget(self.expButton)
        self.statusbar.addWidget(self.cmdButton)
        self.statusbar.addWidget(self.imgButton)
        self.statusbar.addWidget(self.findButton)
        #self.statusbar.addWidget(QWidget(self))
        #self.statusbar.addWidget(self.zoominButton)
        #self.statusbar.addWidget(self.zoomoutButton)
        self.statusbar.addWidget(self.statusText)
        self.statusbar.addWidget(self.lineText)
        self.statusbar.addWidget(self.progressbar)
        self.statusbar.addWidget(self.stopButton)
        #self.statusbar.setFixedHeight(18)
        
        ''''Initializing Coloring Style'''
        self.initEditorStyle()
        self.initStyleSheet()
        '''Adding Cental Widget and Status Bar'''
        self.setCentralWidget(self.centralwidget)
        self.setStatusBar(self.statusbar)
        self.textEdit.setReadOnly(True)
        
        
    def initStyleSheet(self):
        import stylesheet
        self.setStyleSheet(stylesheet.mainstyl)
        self.tabWidget.tabBar().setStyleSheet(stylesheet.stletabb)
        self.explorerTabWidget.tabBar().setStyleSheet(stylesheet.stletabb)
        self.outputTabWidget.tabBar().setStyleSheet(stylesheet.stletabb)
        self.popWidget.setStyleSheet(stylesheet.popbg)
        self.popWidget.hide()
        
        
        ''' This is for changing the palette/window colors to Theme '''
    def initEditorStyle(self):
        pass
        #editStyle = config.readStyle()
        #print editStyle
        #pal = QPalette(self.explorerTabWidget.palette())
        #print pal.color(QPalette.Base).name()
        #print pal.color(QPalette.Window).name()
        #pal.setColor(QPalette.Base,self.colorStyle.paper)
        #pal.setColor(QPalette.Text,self.colorStyle.color)
        #self.explorerTabWidget.setPalette(pal)
        #self.outputTabWidget.setPalette(pal)
         
    ''' This is only for testing dont know if it works for builds '''    
    def changeStyleSheet(self):
        ''' Dynamically load the changed stylesheet.py and load the modules and change 
            the style at runtime saves countless deploys '''
        import imp
        foo = imp.load_source('stletabb', workDir+"/stylesheet.py")
        #print foo.stletabb
        #self.setStyleSheet(stylesheet.mainstyl)
        self.tabWidget.tabBar().setStyleSheet(foo.stletabb)
        self.popWidget.setStyleSheet(foo.popbg)
        if(self.popWidget.isHidden()):
            self.popWidget.showPopup()
    
    def build_project(self):
        #current_file = self.files[self.tabWidget.currentIndex()]
        prj = self.treeWidget.getProject()
        if(prj != None):
            self.treeWidget.build(prj)
            
    def run_project(self):
        #current_file = self.files[self.tabWidget.currentIndex()]
        prj = self.treeWidget.getProject()#current_file)
        if(prj != None):
            self.treeWidget.run(prj)
            
    def forceStop(self):
        self.ant.kill()
        self.progressStop()
        
    def kill(self):
        self.deleteLater()
        
#-----------------------------------------------------------------------------------#
#   Menu Actions Functions                                                          #
#-----------------------------------------------------------------------------------#
    def run(self):
        if(config.mode() == 0):
            self.sq.run()
        elif(config.mode() == 1):
            self.adb.run()
        elif(config.mode() == 2):
            self.ios.run()
        elif(config.mode() == 3):
            self.c.run()
            
    def setMode(self, action):
        if(action.text() == "Squ"):
            config.setMode(0)
            self.toolBar.action_Build.setEnabled(False)
            self.toolBar.action_Run.setEnabled(False)
        elif(action.text() == "Emo"):
            config.setMode(1)
            self.toolBar.action_Build.setEnabled(True)
            self.toolBar.action_Run.setEnabled(True)
        elif(action.text() == "Android"):
            config.setMode(2)
            self.toolBar.action_Build.setEnabled(True)
            self.toolBar.action_Run.setEnabled(True)
        elif(action.text() == "ios"):
            config.setMode(3)
            self.toolBar.action_Build.setEnabled(False)
            self.toolBar.action_Run.setEnabled(False)
            
    def openCommand(self):
        text, ok = QInputDialog.getText(self, 'Run Command', 'Command:')
        cmd = str(text)
        if ok and cmd != "":
            import subprocess
            subprocess.Popen(cmd)
     
    def about(self):
        form = DialogAbout(self)
        
    def todo(self):
        form = DialogTodo(self)
        form.show()

    def help(self):
        QMessageBox.about(self,"Help","This is about all The Help that i can Give you now")
        
    def full(self):
        if not self.isFull:
            self.setWindowState(Qt.WindowFullScreen)
            self.isFull = True
        else:
            self.setWindowState(Qt.WindowMaximized)
            self.isFull = False
            
    def android(self):
        form = DialogAndroid(self)
        form.show()
    
    def antt(self):
        form = DialogAnt(self)
        form.show()
        
    def squirrel(self):
        form = DialogSquirrel(self)
        form.show()
        
    def findBarShow(self):
        if(self.findLayoutWidget.isHidden()):
            self.findLayoutWidget.show()
        else:
            self.findLayoutWidget.hide()
            
    def exp(self):
        if(self.explorerTabWidget.isHidden()):
            self.explorerTabWidget.show()
        else:
            self.explorerTabWidget.hide()
    
    def cmd(self):
        if(self.outputTabWidget.isHidden()):
            self.outputTabWidget.show()
        else:
            self.outputTabWidget.hide()
            
    def editor(self):
        if(self.editorLayoutWidget.isHidden()):
            self.editorLayoutWidget.show()
            self.levelLayoutWidget.hide()
            self.designerLayoutWidget.hide()
            
    def design(self):
        if(self.designerLayoutWidget.isHidden()):
            self.designerLayoutWidget.show()
            self.editorLayoutWidget.hide()
            self.levelLayoutWidget.hide()
        else:
            self.designerLayoutWidget.hide()
            self.editorLayoutWidget.show()
            
    def level(self):
        if(self.levelLayoutWidget.isHidden()):
            self.levelLayoutWidget.show()
            self.editorLayoutWidget.hide()
            self.designerLayoutWidget.hide()
        else:
            self.levelLayoutWidget.hide()
            self.editorLayoutWidget.show()
            
    def closeDesigner(self,no):
        pass
        '''
        if(no == self.tiler.closeIndex()):
            if(self.tiler.isHidden()):
                self.tiler.show()
            else:
                self.tiler.setCurrentIndex(1)
                self.tiler.hide()
        '''
     
    '''The current Changed idx of outputTabWidget is passed to this a param'''   
    def closeConsole(self,no = 2):
        if(no == 2):
            if(self.outputTabWidget.isHidden()):
                self.outputTabWidget.show()
            else:
                self.outputTabWidget.setCurrentIndex(1)
                self.outputTabWidget.hide()
                
    def popOutput(self):
        if(self.outputTabWidget.isHidden()):
            self.outputTabWidget.show()
        self.outputTabWidget.setCurrentIndex(1)
    
    def popError(self):
        if(self.outputTabWidget.isHidden()):
            self.outputTabWidget.show()
        self.outputTabWidget.setCurrentIndex(0)
        
    '''The current Changed idx of explorerTabWidget is passed to this a param'''
    def closeExplorer(self,no = 2):
        if(no == 2):
            if(self.explorerTabWidget.isHidden()):
                self.explorerTabWidget.show()
            else:
                self.explorerTabWidget.setCurrentIndex(0)
                self.explorerTabWidget.hide()
        elif(no == 1):
            self.fileChanged(no)
                
    ''' This is to refresh the outline widget'''
    def fileChanged(self,no):
        if(self.explorerTabWidget.currentIndex() == 1):
            edt = self.tabWidget.widget(self.tabWidget.currentIndex())
            source = edt.text()
            self.outlineWidget.parseText(source)
           
    def statusSaving(self):
        self.statusText.setText("Saving")   
    def statusParsing(self):
        self.statusText.setText("Parsing")   
    def statusWriting(self):
        self.statusText.setText("Writable")  
    def statusRunning(self):
        self.statusText.setText("Running")   
    def statusStopping(self):
        self.statusText.setText("Stopping")
    def statusCommand(self):
        self.statusText.setText("Command")
    def statusBuilding(self):
        self.statusText.setText("Building")
    def statusInstalling(self):
        self.statusText.setText("Installing")
    def statusCleaning(self):
        self.statusText.setText("Cleaning")
    def statusCreating(self):
        self.statusText.setText("Creating")
                
    def progressStart(self):
        self.progress == True
        self.temp == True
        if(self.progressbar.isHidden()):
            self.progressbar.show()
        if(self.stopButton.isHidden()):
            self.stopButton.show()
        self.progressbar.setValue(1)
        
    def progressStop(self):
        self.progress == False
        self.temp == False
        self.progressbar.setValue(100)
        if not(self.progressbar.isHidden()):
            self.progressbar.hide()
        if not(self.stopButton.isHidden()):
            self.stopButton.hide()
              
    def progressUpdate(self):
        if(self.progress):
            if(self.temp):
                self.counter += 1
                self.progressbar.setValue(self.counter)
                if(self.counter == 100):
                    self.temp = False
            else:
                self.counter -= 1
                self.progressbar.setValue(self.counter)
                if(self.counter == 0):
                    self.temp = True
            
                
#-----------------------------------------------------------------------------------#
#   Editor Functions                                                                #
#-----------------------------------------------------------------------------------#   
    '''Search and Replace Functions'''  
    def findCurrentText(self):
        edt = self.tabWidget.widget(self.tabWidget.currentIndex())
        edt.findText(self.lineEdit.text(),self.regex.isChecked(),self.caseSensitive.isChecked(),self.wholeWord.isChecked(),self.backward.isChecked())
    def replaceCurrentText(self):
        edt = self.tabWidget.widget(self.tabWidget.currentIndex())
        edt.replaceText(self.lineEdit_2.text()) 
    def replaceFindText(self):
        edt = self.tabWidget.widget(self.tabWidget.currentIndex())
        edt.replaceText(self.lineEdit_2.text())
        self.findCurrentText()      
    def replaceAllText(self):
        edt = self.tabWidget.widget(self.tabWidget.currentIndex())
        while(edt.findText(self.lineEdit.text(),self.regex.isChecked(),self.caseSensitive.isChecked(),self.wholeWord.isChecked(),self.backward.isChecked())):
            edt.replaceText(self.lineEdit_2.text())      
    def errorLine(self,error):
        index = self.tabWidget.currentIndex()
        edt = self.tabWidget.widget(index)
        '''To prevent File item double clicking'''
        if(error.isFile() == False):
            edt.setLine(error.line)
            
    '''Font Functions'''       
    def zoomin(self):
        pass
        #for i in range(len(self.files)):
        #    self.tabWidget.widget(i).zoomin()
    def zoomout(self):
        pass
        #for i in range(len(self.files)):
        #    self.tabWidget.widget(i).zoomout()
            
    ''' Must implement Lexer '''
    def setLexer(self, action):
        pass
        #print action.text()
    
    def setApi(self, action):
        #print action.text()
        for i in range(len(self.files)): #not QString
            self.tabWidget.widget(i).setApi(str(action.text()))
    
    def setFont(self,font):
        config.setFontName(str(font.family()))
        for i in range(len(self.files)):
            self.tabWidget.widget(i).setNewFont(font)
            
    def setFontSize(self,idx):
        fontSize = idx+1
        config.setFontSize(fontSize)
        for i in range(len(self.files)):
            self.tabWidget.widget(i).setFontSize() 
            
    def gotoLine(self,item):
        edt = self.tabWidget.widget(self.tabWidget.currentIndex())
        edt.setLine(item.line)
        
    def updateLine(self,no,col):
        self.lineText.setText(str(no)+" : "+str(col))
            
    def setMargin(self):
        mar = config.margin()
        if(mar == 0): 
            config.setMargin(1)
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setMargin(1)
        else:
            config.setMargin(0)
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setMargin(0)
                
    ''' Toggle '''
    def setIndent(self):
        indent = config.indent()
        if(indent == 0): 
            config.setIndent(1)
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setIndent(1)
        else:
            config.setIndent(0)
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setIndent(0)
    ''' Toggle '''         
    def setWhiteSpace(self):
        white = config.whiteSpace()
        if(white == 0): 
            config.setWhiteSpace(1)
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setWhitespaceVisibility(True)
        else:
            config.setWhiteSpace(0)
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setWhitespaceVisibility(False)
    
    ''' Toggle '''         
    def setEndLine(self):
        for i in range(len(self.files)):
            edt = self.tabWidget.widget(i)
            edt.setEolVisibility(not edt.eolVisibility())
                
    def setEncoding(self, action):
        if(action.text() == "Ascii"):
            config.setAscii()
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setUtf8(False)
        elif(action.text() == "Unicode"):
            config.setUnicode()
            for i in range(len(self.files)):
                self.tabWidget.widget(i).setUtf8(True)
        
    def setThreshold(self,val):
        config.setThresh(val)
        for i in range(len(self.files)):
            #print i
            self.tabWidget.widget(i).setThreshold(val)
    def setTabWidth(self,val):
        config.setTabWidth(val)
        for i in range(len(self.files)):
            #print i
            self.tabWidget.widget(i).setTabWidth(val)
    '''style Functions'''         
#-----------------------------------------------------------------------------------#
#   Command Functions                                                               #
#-----------------------------------------------------------------------------------#   
    def getFile(self):
        self.browsedialog = DialogBrowse(self)
        self.browsedialog.tree.itemDoubleClicked.connect(self.getName)
        self.browsedialog.show()
            
    def getName(self,item):
        if(item.isFile()):
                self.browsedialog.accept()
                fname = item.getPath()
                if not (fname == ""):
                    index = self.combo2.currentIndex()
                    text = str(self.combo2.itemText(index))+" "+fname
                    self.combo2.setItemText(index,text)
                    self.paramList.pop(index)
                    self.paramList.insert(index,text)
                    config.setParam(self.paramList)
                 
    def addCmd(self,index):
        text, ok = QInputDialog.getText(self, 'Add Command', 'Command:')
        if(ok):
            if(str(text) != ''):
                cmd = str(text).upper()
                self.cmdList.append(cmd)
                #print self.cmdList
                self.combo.addItem(cmd)
                config.setCmd(self.cmdList)
                config.setParam(self.paramList)
                
    def delCmd(self):
        index = self.combo.currentIndex()
        self.combo.removeItem(index)
        self.cmdList.pop(index)
        #print self.cmdList
        config.setCmd(self.cmdList)
        
    def addParam(self,index):
        text, ok = QInputDialog.getText(self, 'Add Parameters', 'Params:')
        if(ok):
            if(str(text) != ''):
                param = str(text)
                self.paramList.append(param)
                self.combo2.addItem(param)
                config.setParam(self.paramList)
                
    def delParam(self):
        index = self.combo2.currentIndex()
        self.combo2.removeItem(index)
        self.paramList.pop(index)
        config.setParam(self.paramList)
        
    def checkHasValue(self,list):
        if(list != None and len(list) != 0):
            return True
        else:
            return False
Exemplo n.º 19
0
class XLoaderWidget(QWidget):
    """ """
    Mode    = enum('Spinner', 'Progress')
    MOVIE   = None
    
    def __init__( self, parent = None, style='gray' ):
        super(XLoaderWidget, self).__init__( parent )
        
        # define properties
        self._currentMode       = None
        self._showSubProgress   = False
        
        self.setAttribute(Qt.WA_DeleteOnClose)
        
        # udpate the palette
        palette = self.palette()
        
        if style == 'white':
            palette.setColor( palette.Window, QColor(255, 255, 255, 180) )
        else:
            palette.setColor( palette.Window, QColor( 80, 80, 80, 180 ) )
        
        palette.setColor( palette.Base, Qt.gray )
        palette.setColor( palette.AlternateBase, Qt.lightGray )
        palette.setColor( palette.WindowText, Qt.gray )
        self.setPalette(palette)
        
        # create the movie label
        self._movieLabel = QLabel(self)
        self._movieLabel.setAlignment(Qt.AlignCenter)
        self._movieLabel.setMovie(XLoaderWidget.getMovie())
        self._movieLabel.setPalette(palette)
        
        self._smallMovieLabel = QLabel(self)
        self._smallMovieLabel.setAlignment(Qt.AlignCenter)
        self._smallMovieLabel.setMovie(XLoaderWidget.getMovie())
        self._smallMovieLabel.setPalette(palette)
        self._smallMovieLabel.hide()
        
        # create text label
        self._messageLabel = QLabel(self)
        self._messageLabel.setAlignment(Qt.AlignCenter)
        self._messageLabel.setText('Loading...')
        self._messageLabel.setPalette(palette)
        
        # create primary progress bar
        self._primaryProgressBar    = XLoaderProgressBar(self)
        self._subProgressBar        = XLoaderProgressBar(self)
        
        self._primaryProgressBar.setPalette(palette)
        self._subProgressBar.setPalette(palette)
        
        # create the loader widget
        self._loaderFrame = QFrame(self)
        self._loaderFrame.setFrameShape(QFrame.Box)
        self._loaderFrame.setAutoFillBackground(True)
        self._loaderFrame.setFixedWidth(160)
        self._loaderFrame.setFixedHeight(60)
        
        if style == 'white':
            palette.setColor(palette.Window, QColor('white'))
        else:
            palette.setColor(palette.Window, QColor(85, 85, 85))
        self._loaderFrame.setPalette(palette)
        
        layout = QVBoxLayout()
        layout.addWidget(self._movieLabel)
        layout.addWidget(self._primaryProgressBar)
        layout.addWidget(self._subProgressBar)
        layout.addStretch()
        layout.addWidget(self._messageLabel)
        self._loaderFrame.setLayout(layout)
        
        # set default properties
        self.setAutoFillBackground(True)
        
        # layout the controls
        layout = QVBoxLayout()
        layout.addStretch(1)
        layout.addWidget(self._loaderFrame)
        layout.addWidget(self._smallMovieLabel)
        layout.addStretch(1)
        
        hlayout = QHBoxLayout()
        hlayout.addStretch(1)
        hlayout.addLayout(layout)
        hlayout.addStretch(1)
        
        self.setLayout(hlayout)
        self.setCurrentMode(XLoaderWidget.Mode.Spinner)
        
        # create connections
    
    def currentMode( self ):
        """
        Returns the current mode that this loader's in.
        
        :return     <XLoaderWidget.Mode>
        """
        return self._currentMode
    
    def eventFilter( self, object, event ):
        """
        Resizes this widget with the parent when its resize event is triggered.
        
        :param      object | <QObject>
                    event  | <QEvent>
        
        :return     <bool> | consumed
        """
        if event.type() == event.Resize:
            self.resize(event.size())
            
        elif event.type() == event.Move:
            self.move(event.pos())
        
        elif event.type() == event.Close:
            self.setParent(None)
            self.deleteLater()
        
        return False
    
    def increment( self, amount=1):
        """
        Increments the main progress bar by amount.
        """
        self._primaryProgressBar.setValue(self.value() + amount)
    
    def incrementSub( self, amount=1):
        """
        Increments the sub-progress bar by amount.
        """
        self._subProgressBar.setValue(self.subValue() + amount)
    
    def message( self ):
        """
        Returns the current message being displayed in the loader.
        
        :return     <str>
        """
        return self._messageLabel.text()
    
    def movie(self):
        """
        Returns the movie linked with this loader.
        
        :return     <QMovie>
        """
        return self._movieLabel.movie()
    
    def resize(self, size):
        """
        Handles when the loader is too small for an area.
        
        :param      event | <QResizeEvent>
        """
        super(XLoaderWidget, self).resize(size)
        
        # show small loader
        if size.width() < self._loaderFrame.width() or \
           size.height() < self._loaderFrame.height():
            self._loaderFrame.hide()
            self._smallMovieLabel.show()
        
        # show regular loader
        else:
            self._loaderFrame.show()
            self._smallMovieLabel.hide()
    
    def subValue( self ):
        """
        Returns the value of the sub progress bar.
        
        :return     <int>
        """
        return self._subProgressBar.value()
    
    def setCurrentMode( self, mode ):
        """
        Sets what mode this loader will be in.
        
        :param      mode | <XLoaderWidget.Mode>
        """
        if ( mode == self._currentMode ):
            return
            
        self._currentMode = mode
        
        ajax = mode == XLoaderWidget.Mode.Spinner
        self._movieLabel.setVisible(ajax)
        self._primaryProgressBar.setVisible(not ajax)
        self._subProgressBar.setVisible(not ajax and self._showSubProgress)
    
    def setMessage( self, message ):
        """
        Sets the loading message to display.
        
        :param      message | <str>
        """
        self._messageLabel.setText(message)
    
    def setMovie( self, movie ):
        """
        Sets the movie for this loader to the inputed movie.
        
        :param      movie | <QMovie>
        """
        self._movieLabel.setMovie(movie)
        self._smallMovieLabel.setMovie(movie)
    
    def setTotal( self, amount ):
        """
        Sets the total amount for the main progress bar.
        
        :param      amount | <int>
        """
        self._primaryProgressBar.setValue(0)
        self._primaryProgressBar.setMaximum(amount)
        
        if amount:
            self.setCurrentMode(XLoaderWidget.Mode.Progress)
    
    def setSubTotal( self, amount ):
        """
        Sets the total value for the sub progress bar.
        
        :param      amount | <int>
        """
        self._subProgressBar.setValue(0)
        self._subProgressBar.setMaximum(amount)
        if amount:
            self.setShowSubProgress(True)
    
    def setSubValue( self, value ):
        """
        Sets the current value for the sub progress bar.
        
        :param      value | <int>
        """
        self._subProgressBar.setValue(value)
        
    def setShowSubProgress( self, state ):
        """
        Toggles whether or not the sub progress bar should be visible.
        
        :param      state | <bool>
        """
        ajax = self.currentMode() == XLoaderWidget.Mode.Spinner
        self._showSubProgress = state
        self._subProgressBar.setVisible(not ajax and state)
    
    def setValue(self, value):
        """
        Sets the current value for the primary progress bar.
        
        :param      value | <int>
        """
        self._primaryProgressBar.setValue(value)
    
    def showSubProgress( self ):
        """
        Returns whether or not the sub progress bar is visible when not in
        ajax mode.
        
        :return     <bool>
        """
        return self._showSubProgress
    
    def subValue( self ):
        """
        Returns the sub value for this loader.
        
        :return     <int>
        """
        return self._subProgressBar.value()
    
    def value( self ):
        """
        Returns the value for the primary progress bar.
        
        :return     <int>
        """
        return self._primaryProgressBar.value()
    
    @staticmethod
    def getMovie():
        """
        Returns the movie instance for the loader widget.
        
        :return     <QMovie>
        """
        if not XLoaderWidget.MOVIE:
            filename = projexui.resources.find('img/ajax_loader.gif')
            XLoaderWidget.MOVIE = QMovie()
            XLoaderWidget.MOVIE.setFileName(filename)
            XLoaderWidget.MOVIE.start()
        
        return XLoaderWidget.MOVIE
    
    @staticmethod
    def start(widget, processEvents=True, style=None, movie=None):
        """
        Starts a loader widget on the inputed widget.
        
        :param      widget          | <QWidget>
        
        :return     <XLoaderWidget>
        """
        if style is None:
            style = os.environ.get('PROJEXUI_LOADER_STYLE', 'gray')
        
        # there is a bug with the way the loader is handled in a splitter,
        # so bypass it
        parent = widget.parent()
        while isinstance(parent, QSplitter):
            parent = parent.parent()
        
        # retrieve the loader widget
        loader = getattr(widget, '_private_xloader_widget', None)
        if not loader:
            loader = XLoaderWidget(parent, style)
            
            # make sure that if the widget is destroyed, the loader closes
            widget.destroyed.connect(loader.deleteLater)
            
            setattr(widget, '_private_xloader_widget', loader)
            setattr(widget, '_private_xloader_count', 0)
            
            loader.move(widget.pos())
            if widget.isVisible():
                loader.show()
            
            if movie:
                loader.setMovie(movie)
            
            widget.installEventFilter(loader)
        else:
            count = getattr(widget, '_private_xloader_count', 0)
            setattr(widget, '_private_xloader_count', count + 1)
        
        loader.resize(widget.size())
        return loader
    
    @staticmethod
    def stop(widget, force=False):
        """
        Stops a loader widget on the inputed widget.
        
        :param      widget | <QWidget>
        """
        # make sure we have a widget to stop
        loader = getattr(widget, '_private_xloader_widget', None)
        if not loader:
            return
        
        # decrement the number of times this loader was created for the widget
        # to allow for stacked closure
        count = getattr(widget, '_private_xloader_count', 0)
        if force or count <= 1:
            # close out the loader widget
            setattr(widget, '_private_xloader_count', 0)
            setattr(widget, '_private_xloader_widget', None)
            
            loader.close()
            loader.setParent(None)
            loader.deleteLater()
        else:
            setattr(widget, '_private_xloader_count', count - 1)
    
    @staticmethod
    def stopAll(widget):
        """
        Stops all loader widgets from this parent down, cleaning out the \
        memory for them.
        
        :param      widget  | <QWidget>
        """
        for loader in widget.findChildren(XLoaderWidget):
            loader.setParent(None)
            loader.deleteLater()
class AudioVideoTab(QWidget):
    def __init__(self, parent):
        super(AudioVideoTab, self).__init__(parent)
        self.parent = parent
        self.name = "AudioVideo"

        self.defaultStr = self.tr("Default")
        self.DisableStream = self.tr("Disable")

        self.formats = config.video_formats
        frequency_values = [self.defaultStr] + config.video_frequency_values
        bitrate_values = [self.defaultStr] + config.video_bitrate_values

        rotation_options = [
            self.tr("None"),
            "90 " + self.tr("clockwise"),
            "90 " + self.tr("clockwise") + " + " + self.tr("vertical flip"),
            "90 " + self.tr("counter clockwise"),
            "90 " + self.tr("counter clockwise") + " + " + self.tr("vertical flip"),
            "180",
            self.tr("horizontal flip"),
            self.tr("vertical flip"),
        ]

        digits_validator = QRegExpValidator(QRegExp(r"[1-9]\d*"), self)
        digits_validator_wzero = QRegExpValidator(QRegExp(r"\d*"), self)
        digits_validator_minus = QRegExpValidator(QRegExp(r"(-1|[1-9]\d*)"), self)
        time_validator = QRegExpValidator(QRegExp(r"\d{1,2}:\d{1,2}:\d{1,2}\.\d+"), self)

        converttoQL = QLabel(self.tr("Convert to:"))
        self.extQCB = QComboBox()
        self.extQCB.setMinimumWidth(100)
        vidcodecQL = QLabel("Video codec:")
        self.vidcodecQCB = QComboBox()
        self.vidcodecQCB.setMinimumWidth(110)
        audcodecQL = QLabel("Audio codec:")
        self.audcodecQCB = QComboBox()
        self.audcodecQCB.setMinimumWidth(110)

        hlayout1 = utils.add_to_layout(
            "h",
            converttoQL,
            self.extQCB,
            QSpacerItem(180, 20),
            vidcodecQL,
            self.vidcodecQCB,
            audcodecQL,
            self.audcodecQCB,
        )

        commandQL = QLabel(self.tr("Command:"))
        self.commandQLE = QLineEdit()
        self.presetQPB = QPushButton(self.tr("Preset"))
        self.defaultQPB = QPushButton(self.defaultStr)
        hlayout2 = utils.add_to_layout("h", commandQL, self.commandQLE, self.presetQPB, self.defaultQPB)

        sizeQL = QLabel(self.tr("Video Size:"))
        aspectQL = QLabel(self.tr("Aspect:"))
        frameQL = QLabel(self.tr("Frame Rate (fps):"))
        bitrateQL = QLabel(self.tr("Video Bitrate (kbps):"))

        self.widthQLE = utils.create_LineEdit((70, 16777215), digits_validator_minus, 4)
        self.heightQLE = utils.create_LineEdit((70, 16777215), digits_validator_minus, 4)
        label = QLabel('<html><p align="center">x</p></html>')
        layout1 = utils.add_to_layout("h", self.widthQLE, label, self.heightQLE)
        self.aspect1QLE = utils.create_LineEdit((50, 16777215), digits_validator, 2)
        self.aspect2QLE = utils.create_LineEdit((50, 16777215), digits_validator, 2)
        label = QLabel('<html><p align="center">:</p></html>')
        layout2 = utils.add_to_layout("h", self.aspect1QLE, label, self.aspect2QLE)
        self.frameQLE = utils.create_LineEdit((120, 16777215), digits_validator, 4)
        self.bitrateQLE = utils.create_LineEdit((130, 16777215), digits_validator, 6)

        labels = [sizeQL, aspectQL, frameQL, bitrateQL]
        widgets = [layout1, layout2, self.frameQLE, self.bitrateQLE]

        self.preserveaspectQChB = QCheckBox(self.tr("Preserve aspect ratio"))
        self.preservesizeQChB = QCheckBox(self.tr("Preserve video size"))

        preserve_layout = utils.add_to_layout("v", self.preserveaspectQChB, self.preservesizeQChB)

        videosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            a.setText('<html><p align="center">{0}</p></html>'.format(a.text()))
            layout = utils.add_to_layout("v", a, b)
            videosettings_layout.addLayout(layout)
            if a == aspectQL:
                # add vidaspectCB in layout after aspectQL
                videosettings_layout.addLayout(preserve_layout)

        freqQL = QLabel(self.tr("Frequency (Hz):"))
        chanQL = QLabel(self.tr("Audio Channels:"))
        bitrateQL = QLabel(self.tr("Audio Bitrate (kbps):"))
        threadsQL = QLabel(self.tr("Threads:"))

        self.freqQCB = QComboBox()
        self.freqQCB.addItems(frequency_values)
        self.chan1QRB = QRadioButton("1")
        self.chan1QRB.setMaximumSize(QSize(51, 16777215))
        self.chan2QRB = QRadioButton("2")
        self.chan2QRB.setMaximumSize(QSize(51, 16777215))
        self.group = QButtonGroup()
        self.group.addButton(self.chan1QRB)
        self.group.addButton(self.chan2QRB)
        spcr1 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        spcr2 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        chanlayout = utils.add_to_layout("h", spcr1, self.chan1QRB, self.chan2QRB, spcr2)
        self.audbitrateQCB = QComboBox()
        self.audbitrateQCB.addItems(bitrate_values)
        self.threadsQLE = utils.create_LineEdit((50, 16777215), digits_validator_wzero, 1)

        labels = [freqQL, bitrateQL, chanQL, threadsQL]
        widgets = [self.freqQCB, self.audbitrateQCB, chanlayout, self.threadsQLE]

        audiosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            a.setText('<html><p align="center">{0}</p></html>'.format(a.text()))
            layout = utils.add_to_layout("v", a, b)
            audiosettings_layout.addLayout(layout)

        time_format = " (hh:mm:ss):"
        beginQL = QLabel(self.tr("Split file. Begin time") + time_format)
        self.beginQLE = utils.create_LineEdit(None, time_validator, None)
        durationQL = QLabel(self.tr("Duration") + time_format)
        self.durationQLE = utils.create_LineEdit(None, time_validator, None)

        hlayout4 = utils.add_to_layout("h", beginQL, self.beginQLE, durationQL, self.durationQLE)

        embedQL = QLabel(self.tr("Embed subtitle:"))
        self.embedQLE = QLineEdit()
        self.embedQTB = QToolButton()
        self.embedQTB.setText("...")

        rotateQL = QLabel(self.tr("Rotate:"))
        self.rotateQCB = QComboBox()
        self.rotateQCB.addItems(rotation_options)

        hlayout5 = utils.add_to_layout("h", rotateQL, self.rotateQCB, embedQL, self.embedQLE, self.embedQTB)

        hidden_layout = utils.add_to_layout("v", videosettings_layout, audiosettings_layout, hlayout4, hlayout5)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.moreQPB = QPushButton(QApplication.translate("Tab", "More"))
        self.moreQPB.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.moreQPB.setCheckable(True)
        hlayout3 = utils.add_to_layout("h", line, self.moreQPB)

        self.frame = QFrame()
        self.frame.setLayout(hidden_layout)
        self.frame.hide()

        final_layout = utils.add_to_layout("v", hlayout1, hlayout2, hlayout3, self.frame)
        self.setLayout(final_layout)

        self.presetQPB.clicked.connect(self.choose_preset)
        self.defaultQPB.clicked.connect(self.set_default_command)
        self.embedQTB.clicked.connect(self.open_subtitle_file)
        self.moreQPB.toggled.connect(self.frame.setVisible)
        self.moreQPB.toggled.connect(lambda: QTimer.singleShot(100, self.resize_parent))
        self.widthQLE.textChanged.connect(self.command_update_size)
        self.heightQLE.textChanged.connect(self.command_update_size)
        self.aspect1QLE.textChanged.connect(self.command_update_aspect)
        self.aspect2QLE.textChanged.connect(self.command_update_aspect)
        self.frameQLE.textChanged.connect(self.command_update_frames)
        self.bitrateQLE.textChanged.connect(self.command_update_vidbitrate)
        self.threadsQLE.textChanged.connect(self.command_update_threads)
        self.beginQLE.textChanged.connect(self.command_update_begin_time)
        self.durationQLE.textChanged.connect(self.command_update_duration)
        self.embedQLE.textChanged.connect(self.command_update_subtitles)
        self.vidcodecQCB.currentIndexChanged.connect(self.command_update_vcodec)
        self.audcodecQCB.currentIndexChanged.connect(self.command_update_acodec)
        self.freqQCB.currentIndexChanged.connect(self.command_update_frequency)
        self.rotateQCB.currentIndexChanged.connect(self.command_update_rotation)
        self.audbitrateQCB.currentIndexChanged.connect(self.command_update_audbitrate)
        self.chan1QRB.clicked.connect(lambda: self.command_update_channels("1"))
        self.chan2QRB.clicked.connect(lambda: self.command_update_channels("2"))
        self.preserveaspectQChB.toggled.connect(self.command_update_preserve_aspect)
        self.preservesizeQChB.toggled.connect(self.command_update_preserve_size)

    def resize_parent(self):
        """Give MainWindow its default size."""
        self.parent.setMinimumSize(self.parent.sizeHint())
        self.parent.resize(self.parent.sizeHint())

    def clear(self):
        """Clear all values of graphical widgets."""
        lines = [
            self.commandQLE,
            self.widthQLE,
            self.heightQLE,
            self.aspect1QLE,
            self.aspect2QLE,
            self.frameQLE,
            self.bitrateQLE,
            self.threadsQLE,
            self.beginQLE,
            self.embedQLE,
            self.durationQLE,
        ]
        for i in lines:
            i.clear()

        self.vidcodecQCB.setCurrentIndex(0)
        self.audcodecQCB.setCurrentIndex(0)
        self.freqQCB.setCurrentIndex(0)
        self.audbitrateQCB.setCurrentIndex(0)
        self.rotateQCB.setCurrentIndex(0)
        self.preserveaspectQChB.setChecked(False)
        self.preservesizeQChB.setChecked(False)
        self.group.setExclusive(False)
        self.chan1QRB.setChecked(False)
        self.chan2QRB.setChecked(False)
        self.group.setExclusive(True)
        # setExclusive(False) in order to be able to uncheck checkboxes and
        # then setExclusive(True) so only one radio button can be set

    def fill_video_comboboxes(self, vcodecs, acodecs, extraformats):
        vcodecs = [i for i in vcodecs.split("\n")] if vcodecs else []
        acodecs = [i for i in acodecs.split("\n")] if acodecs else []
        extraformats = [i for i in extraformats.split("\n")] if extraformats else []

        self.vidcodecQCB.currentIndexChanged.disconnect()
        self.audcodecQCB.currentIndexChanged.disconnect()

        self.vidcodecQCB.clear()
        self.audcodecQCB.clear()
        self.extQCB.clear()
        self.vidcodecQCB.addItems([self.defaultStr, self.DisableStream] + vcodecs)
        self.audcodecQCB.addItems([self.defaultStr, self.DisableStream] + acodecs)
        self.extQCB.addItems(sorted(self.formats + extraformats))

        self.vidcodecQCB.currentIndexChanged.connect(self.command_update_vcodec)
        self.audcodecQCB.currentIndexChanged.connect(self.command_update_acodec)

    def ok_to_continue(self):
        """
        Check if everything is ok with audiovideotab to continue conversion.

        Check if:
        - Either ffmpeg or avconv are installed.

        Return True if all tests pass, else False.
        """
        if self.parent.vidconverter is None:
            QMessageBox.warning(
                self,
                "FF Multi Converter - " + self.tr("Error!"),
                self.tr(
                    "Neither ffmpeg nor libav are installed."
                    "\nYou will not be able to convert audio/video files until you"
                    " install one of them."
                ),
            )
            return False
        return True

    def open_subtitle_file(self):
        """
        Get the filename using standard QtDialog and update embedQLE's text.
        """
        fname = QFileDialog.getOpenFileName(
            self, "FF Multi Converter - " + self.tr("Choose File"), config.home, "Subtitles (*.srt *.sub *.ssa *.ass)"
        )
        if fname:
            self.embedQLE.setText(fname)

    def set_default_command(self):
        """Set the default value to self.commandQLE."""
        self.clear()
        self.commandQLE.setText(self.parent.default_command)

    def choose_preset(self):
        """
        Open the presets dialog and update self.commandQLE,
        and self.extQCB and with the appropriate values.
        """
        dialog = presets_dlgs.ShowPresets(choose=True)
        if dialog.exec_() and dialog.the_command is not None:
            self.clear()
            self.commandQLE.setText(dialog.the_command)
            self.commandQLE.home(False)
            find = self.extQCB.findText(dialog.the_extension)
            if find >= 0:
                self.extQCB.setCurrentIndex(find)

    def command_update_size(self):
        command = self.commandQLE.text()
        text1 = self.widthQLE.text()
        text2 = self.heightQLE.text()

        if not (text1 == "-1" or text2 == "-1"):
            self.preserveaspectQChB.setChecked(False)

        if (text1 or text2) and not (text1 and text2) or (text1 == "-" or text2 == "-"):
            return

        regex = r"(\s+|^)-s(:v){0,1}\s+\d+x\d+(\s+|$)"
        if re.search(regex, command):
            command = re.sub(regex, "", command)

        regex = r"(,*\s*){0,1}(scale=-?\d+:-?\d+)(\s*,*\s*){0,1}"
        _filter = "scale={0}:{1}".format(text1, text2) if text1 and text2 else ""

        self.commandQLE.setText(utils.update_cmdline_text(command, _filter, regex, bool(text1 and text2), 0, 2))

    def command_update_preserve_size(self):
        checked = self.preservesizeQChB.isChecked()

        self.widthQLE.setEnabled(not checked)
        self.heightQLE.setEnabled(not checked)

        if checked:
            self.widthQLE.clear()
            self.heightQLE.clear()
            # command_update_size() is triggered here

        command = self.commandQLE.text()
        regex = r"(\s+|^)-s\s+\d+x\d+(\s+|$)"
        command = re.sub(" +", " ", re.sub(regex, " ", command)).strip()
        self.commandQLE.setText(command)

    def command_update_aspect(self):
        command = self.commandQLE.text()
        text1 = self.aspect1QLE.text()
        text2 = self.aspect2QLE.text()

        if (text1 or text2) and not (text1 and text2):
            return

        regex = r"(\s+|^)-aspect\s+\d+:\d+(\s+|$)"
        s = " -aspect {0}:{1} ".format(text1, text2) if text1 and text2 else " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_preserve_aspect(self):
        command = self.commandQLE.text()
        checked = self.preserveaspectQChB.isChecked()

        self.aspect1QLE.setEnabled(not checked)
        self.aspect2QLE.setEnabled(not checked)

        if checked:
            self.aspect1QLE.clear()
            self.aspect2QLE.clear()
            # self.command_update_aspect() is triggered here

            regex = r"(,*\s*){0,1}(scale=(-?\d+):(-?\d+))(\s*,*\s*){0,1}"
            search = re.search(regex, command)
            if search:
                width = search.groups()[2]
                height = search.groups()[3]
                if not (width == "-1" or height == "-1"):
                    s = "scale=-1:{0}".format(height)
                    command = re.sub(regex, r"\1{0}\5".format(s), command)
                    self.widthQLE.setText("-1")
                    self.heightQLE.setText(height)

        regex = r"(\s+|^)-aspect\s+\d+:\d+(\s+|$)"
        command = re.sub(" +", " ", re.sub(regex, " ", command)).strip()
        self.commandQLE.setText(command)

    def command_update_frames(self):
        command = self.commandQLE.text()
        text = self.frameQLE.text()

        regex = r"(\s+|^)-r\s+\d+(\s+|$)"
        s = " -r {0} ".format(text) if text else " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_vidbitrate(self):
        command = self.commandQLE.text()
        text = self.bitrateQLE.text()

        regex = r"(\s+|^)-b(:v){0,1}\s+\d+[kKmM](\s+|$)"
        s = " -b:v {0}k ".format(text) if text else " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub("-sameq", "", command)
        command = re.sub(" +", " ", command).strip()

        self.commandQLE.setText(command)

    def command_update_frequency(self):
        command = self.commandQLE.text()
        text = self.freqQCB.currentText()

        regex = r"(\s+|^)-ar\s+\d+(\s+|$)"
        s = " -ar {0} ".format(text) if self.freqQCB.currentIndex() != 0 else " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_audbitrate(self):
        command = self.commandQLE.text()
        text = self.audbitrateQCB.currentText()

        regex = r"(\s+|^)-(ab|b:a)\s+\d+[kKmM](\s+|$)"
        if self.audbitrateQCB.currentIndex() != 0:
            s = " -b:a {0}k ".format(text)
        else:
            s = " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_channels(self, channel):
        command = self.commandQLE.text()

        regex = r"(\s+|^)-ac\s+\d+(\s+|$)"
        s = " -ac {0} ".format(channel)

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_threads(self):
        command = self.commandQLE.text()
        text = self.threadsQLE.text()

        regex = r"(\s+|^)-threads\s+\d+(\s+|$)"
        s = " -threads {0} ".format(text) if text else " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_begin_time(self):
        command = self.commandQLE.text()
        text = self.beginQLE.text()

        regex = r"(\s+|^)-ss\s+\S+(\s+|$)"
        s = " -ss {0} ".format(text) if text else " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_duration(self):
        command = self.commandQLE.text()
        text = self.durationQLE.text()

        regex = r"(\s+|^)-t\s+\S+(\s+|$)"
        s = " -t {0} ".format(text) if text else " "

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_vcodec(self):
        command = self.commandQLE.text()
        text = self.vidcodecQCB.currentText()

        regex = r"(\s+|^)-(vcodec|c:v)\s+\S+(\s+|$)"
        regex_vn = r"(\s+|^)-vn(\s+|$)"
        if self.vidcodecQCB.currentIndex() == 1:
            s = " -vn ".format(text)
        elif self.vidcodecQCB.currentIndex() == 0:
            s = " "
        else:
            s = " -vcodec {0} ".format(text)

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        elif re.search(regex_vn, command):
            command = re.sub(regex_vn, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_acodec(self):
        command = self.commandQLE.text()
        text = self.audcodecQCB.currentText()

        regex = r"(\s+|^)-(acodec|c:a)\s+\S+(\s+|$)"
        regex_an = r"(\s+|^)-an(\s+|$)"
        if self.audcodecQCB.currentIndex() == 1:
            s = " -an ".format(text)
        elif self.audcodecQCB.currentIndex() == 0:
            s = " "
        else:
            s = " -acodec {0} ".format(text)

        if re.search(regex, command):
            command = re.sub(regex, s, command)
        elif re.search(regex_an, command):
            command = re.sub(regex_an, s, command)
        else:
            command += s

        command = re.sub(" +", " ", command).strip()
        self.commandQLE.setText(command)

    def command_update_subtitles(self):
        command = self.commandQLE.text()
        regex = r"(,*\s*){0,1}(subtitles=\'.*\')(\s*,*\s*){0,1}"

        text = self.embedQLE.text()
        _filter = "subtitles='{0}'".format(text) if text else ""

        self.commandQLE.setText(utils.update_cmdline_text(command, _filter, regex, bool(text), 0, 2))

    def command_update_rotation(self):
        command = self.commandQLE.text()
        regex = r"(,*\s*){0,1}(transpose=\d(,\s*transpose=\d)*|vflip|hflip)(\s*,*\s*){0,1}"

        rotate = self.rotateQCB.currentIndex()
        if rotate == 0:  # none
            _filter = ""
        elif rotate == 1:  # 90 clockwise
            _filter = "transpose=1"
        elif rotate == 2:  # 90 clockwise + vertical flip
            _filter = "transpose=3"
        elif rotate == 3:  # 90 counter clockwise
            _filter = "transpose=2"
        elif rotate == 4:  # 90 counter clockwise + vertical flip
            _filter = "transpose=0"
        elif rotate == 5:  # 180
            _filter = "transpose=2,transpose=2"
        elif rotate == 6:  # horizontal flip
            _filter = "hflip"
        elif rotate == 7:  # vertical flip
            _filter = "vflip"

        self.commandQLE.setText(utils.update_cmdline_text(command, _filter, regex, bool(rotate != 0), 0, 3))
Exemplo n.º 21
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self._guiElements = []      # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        #if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground,
                                          self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-red.png"))

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning),
                    ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or \
                (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning,
                                      self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = - self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass


    # ##############################################
    """
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." %
                             str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1
    """

    def restoreWidgetPosition(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored:
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before showEvent and we must not overwrite the the savedGeometry
        # with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            # Restore saved geometry on show
            self.restoreWidgetPosition()
        self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activate_loaded_settings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self):
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarValue = 0

    def progressBarSet(self, value):
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        self.progressBarValueChanged.emit(value)

        if old != value:
            self.progressBarValueChanged.emit(value)

        qApp.processEvents()

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value):
        self.progressBarSet(self.progressBarValue + value)

    def progressBarFinished(self):
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_label.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {"Info": "canvasIcons:information.png",
                    "Warning": "canvasIcons:warning.png",
                    "Error": "canvasIcons:error.png"}
        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconPath[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath("canvasIcons",
                               os.path.join(environ.canvas_install_dir,
                                            "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Exemplo n.º 22
0
class AudioVideoTab(QWidget):
    def __init__(self, parent):
        super(AudioVideoTab, self).__init__(parent)
        self.parent = parent
        self.name = 'AudioVideo'
        self.formats = [
            '3gp', 'aac', 'ac3', 'afc', 'aiff', 'amr', 'asf', 'au', 'avi',
            'dvd', 'flac', 'flv', 'mka', 'mkv', 'mmf', 'mov', 'mp3', 'mp4',
            'mpg', 'ogg', 'ogv', 'psp', 'rm', 'spx', 'vob', 'wav', 'webm',
            'wma', 'wmv'
        ]
        self.extra_formats = [
            'aifc', 'm2t', 'm4a', 'm4v', 'mp2', 'mpeg', 'ra', 'ts'
        ]

        nochange = self.tr('No Change')
        frequency_values = [nochange, '22050', '44100', '48000']
        bitrate_values = [
            nochange, '32', '96', '112', '128', '160', '192', '256', '320'
        ]
        pattern = QRegExp(r'^[1-9]\d*')
        validator = QRegExpValidator(pattern, self)

        converttoLabel = QLabel(self.tr('Convert to:'))
        self.extComboBox = QComboBox()
        self.extComboBox.addItems(self.formats + [self.tr('Other')])
        self.extComboBox.setMinimumWidth(130)
        self.extLineEdit = QLineEdit()
        self.extLineEdit.setMaximumWidth(85)
        self.extLineEdit.setEnabled(False)
        hlayout1 = pyqttools.add_to_layout(QHBoxLayout(), converttoLabel, None,
                                           self.extComboBox, self.extLineEdit)
        commandLabel = QLabel(self.tr('Command:'))
        self.commandLineEdit = QLineEdit()
        self.presetButton = QPushButton(self.tr('Preset'))
        self.defaultButton = QPushButton(self.tr('Default'))
        hlayout2 = pyqttools.add_to_layout(QHBoxLayout(), commandLabel,
                                           self.commandLineEdit,
                                           self.presetButton,
                                           self.defaultButton)

        sizeLabel = QLabel(self.tr('Video Size:'))
        aspectLabel = QLabel(self.tr('Aspect:'))
        frameLabel = QLabel(self.tr('Frame Rate (fps):'))
        bitrateLabel = QLabel(self.tr('Video Bitrate (kbps):'))

        self.widthLineEdit = pyqttools.create_LineEdit((50, 16777215),
                                                       validator, 4)
        self.heightLineEdit = pyqttools.create_LineEdit((50, 16777215),
                                                        validator, 4)
        label = QLabel('x')
        layout1 = pyqttools.add_to_layout(QHBoxLayout(), self.widthLineEdit,
                                          label, self.heightLineEdit)
        self.aspect1LineEdit = pyqttools.create_LineEdit((35, 16777215),
                                                         validator, 2)
        self.aspect2LineEdit = pyqttools.create_LineEdit((35, 16777215),
                                                         validator, 2)
        label = QLabel(':')
        layout2 = pyqttools.add_to_layout(QHBoxLayout(), self.aspect1LineEdit,
                                          label, self.aspect2LineEdit)
        self.frameLineEdit = pyqttools.create_LineEdit(None, validator, 4)
        self.bitrateLineEdit = pyqttools.create_LineEdit(None, validator, 6)

        labels = [sizeLabel, aspectLabel, frameLabel, bitrateLabel]
        widgets = [layout1, layout2, self.frameLineEdit, self.bitrateLineEdit]

        videosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            text = a.text()
            a.setText('<html><p align="center">{0}</p></html>'.format(text))
            layout = pyqttools.add_to_layout(QVBoxLayout(), a, b)
            videosettings_layout.addLayout(layout)

        freqLabel = QLabel(self.tr('Frequency (Hz):'))
        chanLabel = QLabel(self.tr('Channels:'))
        bitrateLabel = QLabel(self.tr('Audio Bitrate (kbps):'))

        self.freqComboBox = QComboBox()
        self.freqComboBox.addItems(frequency_values)
        self.chan1RadioButton = QRadioButton('1')
        self.chan1RadioButton.setMaximumSize(QSize(51, 16777215))
        self.chan2RadioButton = QRadioButton('2')
        self.chan2RadioButton.setMaximumSize(QSize(51, 16777215))
        self.group = QButtonGroup()
        self.group.addButton(self.chan1RadioButton)
        self.group.addButton(self.chan2RadioButton)
        spcr1 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        spcr2 = QSpacerItem(40, 20, QSizePolicy.Preferred, QSizePolicy.Minimum)
        chanlayout = pyqttools.add_to_layout(QHBoxLayout(), spcr1,
                                             self.chan1RadioButton,
                                             self.chan2RadioButton, spcr2)
        self.audio_bitrateComboBox = QComboBox()
        self.audio_bitrateComboBox.addItems(bitrate_values)

        labels = [freqLabel, chanLabel, bitrateLabel]
        widgets = [self.freqComboBox, chanlayout, self.audio_bitrateComboBox]

        audiosettings_layout = QHBoxLayout()
        for a, b in zip(labels, widgets):
            text = a.text()
            a.setText('<html><p align="center">{0}</p></html>'.format(text))
            layout = pyqttools.add_to_layout(QVBoxLayout(), a, b)
            audiosettings_layout.addLayout(layout)

        hidden_layout = pyqttools.add_to_layout(QVBoxLayout(),
                                                videosettings_layout,
                                                audiosettings_layout)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.moreButton = QPushButton(QApplication.translate('Tab', 'More'))
        self.moreButton.setSizePolicy(QSizePolicy(QSizePolicy.Fixed))
        self.moreButton.setCheckable(True)
        hlayout3 = pyqttools.add_to_layout(QHBoxLayout(), line,
                                           self.moreButton)

        self.frame = QFrame()
        self.frame.setLayout(hidden_layout)
        self.frame.hide()

        final_layout = pyqttools.add_to_layout(QVBoxLayout(), hlayout1,
                                               hlayout2, hlayout3, self.frame)
        self.setLayout(final_layout)

        self.presetButton.clicked.connect(self.choose_preset)
        self.defaultButton.clicked.connect(self.set_default_command)
        self.moreButton.toggled.connect(self.frame.setVisible)
        self.moreButton.toggled.connect(self.resize_parent)
        self.extComboBox.currentIndexChanged.connect(
            lambda: self.extLineEdit.setEnabled(self.extComboBox.currentIndex(
            ) == len(self.formats)))
        self.widthLineEdit.textChanged.connect(
            lambda: self.command_elements_change('size'))
        self.heightLineEdit.textChanged.connect(
            lambda: self.command_elements_change('size'))
        self.aspect1LineEdit.textChanged.connect(
            lambda: self.command_elements_change('aspect'))
        self.aspect2LineEdit.textChanged.connect(
            lambda: self.command_elements_change('aspect'))
        self.frameLineEdit.textChanged.connect(
            lambda: self.command_elements_change('frames'))
        self.bitrateLineEdit.textChanged.connect(
            lambda: self.command_elements_change('video_bitrate'))
        self.freqComboBox.currentIndexChanged.connect(
            lambda: self.command_elements_change('frequency'))
        self.audio_bitrateComboBox.currentIndexChanged.connect(
            lambda: self.command_elements_change('audio_bitrate'))
        self.chan1RadioButton.clicked.connect(
            lambda: self.command_elements_change('channels1'))
        self.chan2RadioButton.clicked.connect(
            lambda: self.command_elements_change('channels2'))

    def resize_parent(self):
        """Resize MainWindow."""
        height = MAIN_FIXED_HEIGHT if self.frame.isVisible() else MAIN_HEIGHT
        self.parent.setMinimumSize(MAIN_WIDTH, height)
        self.parent.resize(MAIN_WIDTH, height)

    def clear(self):
        """Clear all values of graphical widgets."""
        lines = [
            self.commandLineEdit, self.widthLineEdit, self.heightLineEdit,
            self.aspect1LineEdit, self.aspect2LineEdit, self.frameLineEdit,
            self.bitrateLineEdit, self.extLineEdit
        ]
        for i in lines:
            i.clear()

        self.freqComboBox.setCurrentIndex(0)
        self.audio_bitrateComboBox.setCurrentIndex(0)
        self.group.setExclusive(False)
        self.chan1RadioButton.setChecked(False)
        self.chan2RadioButton.setChecked(False)
        self.group.setExclusive(True)
        # setExclusive(False) in order to be able to uncheck checkboxes and
        # then setExclusive(True) so only one radio button can be set

    def ok_to_continue(self):
        """
        Check if everything is ok with audiovideotab to continue conversion.

        Check if:
        - Either ffmpeg or avconv are installed.
        - Desired extension is valid.
        - self.commandLineEdit is empty.

        Return True if all tests pass, else False.
        """
        if not self.parent.ffmpeg and not self.parent.avconv:
            QMessageBox.warning(
                self, 'FF Multi Converter - ' + self.tr('Error!'),
                self.
                tr('Neither ffmpeg nor avconv are installed.'
                   '\nYou will not be able to convert audio/video files until you'
                   ' install one of them.'))
            return False
        if self.extLineEdit.isEnabled():
            text = str(self.extLineEdit.text()).strip()
            if len(text.split()) != 1 or text[0] == '.':
                QMessageBox.warning(
                    self, 'FF Multi Converter - ' + self.tr('Error!'),
                    self.tr('Extension must be one word and must '
                            'not start with a dot.'))
                self.extLineEdit.selectAll()
                self.extLineEdit.setFocus()
                return False
        if not self.commandLineEdit.text():
            QMessageBox.warning(
                self, 'FF Multi Converter - ' + self.tr('Error!'),
                self.tr('The command LineEdit may not be empty.'))
            self.commandLineEdit.setFocus()
            return False
        return True

    def set_default_command(self):
        """Set the default value to self.commandLineEdit."""
        self.clear()
        self.commandLineEdit.setText(self.parent.default_command)

    def choose_preset(self):
        """
        Open the presets dialog and update self.commandLineEdit,
        self.extComboBox and self.extLineEdit with the appropriate values.
        """
        dialog = presets_dlgs.ShowPresets()
        if dialog.exec_() and dialog.the_command is not None:
            self.commandLineEdit.setText(dialog.the_command)
            self.commandLineEdit.home(False)
            find = self.extComboBox.findText(dialog.the_extension)
            if find >= 0:
                self.extComboBox.setCurrentIndex(find)
            else:
                self.extComboBox.setCurrentIndex(len(self.formats))
                self.extLineEdit.setText(dialog.the_extension)

    def remove_consecutive_spaces(self, string):
        """Remove any consecutive spaces from a string and return it."""
        temp = string
        string = ''
        for i in temp.split():
            if i:
                string += i + ' '
        return string[:-1]

    def command_elements_change(self, widget):
        """Fill self.commandLineEdit with the appropriate command parameters."""
        command = str(self.commandLineEdit.text())

        if widget == 'size':
            text1 = self.widthLineEdit.text()
            text2 = self.heightLineEdit.text()

            if (text1 or text2) and not (text1 and text2):
                return
            f = re.sub(r'^.*(-s\s+\d+x\d+).*$', r'\1', command)
            if re.match(r'^.*(-s\s+\d+x\d+).*$', f):
                command = command.replace(f, '').strip()
            if text1 and text2:
                command += ' -s {0}x{1}'.format(text1, text2)

        elif widget == 'aspect':
            text1 = self.aspect1LineEdit.text()
            text2 = self.aspect2LineEdit.text()

            if (text1 or text2) and not (text1 and text2):
                return
            f = re.sub(r'^.*(-aspect\s+\d+:\d+).*$', r'\1', command)
            if re.match(r'^.*(-aspect\s+\d+:\d+).*$', f):
                command = command.replace(f, '').strip()
            if text1 and text2:
                command += ' -aspect {0}:{1}'.format(text1, text2)

        elif widget == 'frames':
            text = self.frameLineEdit.text()
            f = re.sub(r'^.*(-r\s+\d+).*$', r'\1', command)
            if re.match(r'^.*(-r\s+\d+).*$', f):
                command = command.replace(f, '').strip()
            if text:
                command += ' -r {0}'.format(text)

        elif widget == 'video_bitrate':
            text = self.bitrateLineEdit.text()
            f = re.sub(r'^.*(-b\s+\d+k).*$', r'\1', command)
            if re.match(r'^.*(-b\s+\d+k).*$', f):
                command = command.replace(f, '')
            if text:
                command += ' -b {0}k'.format(text)
            command = command.replace('-sameq', '').strip()

        elif widget == 'frequency':
            text = self.freqComboBox.currentText()
            f = re.sub(r'^.*(-ar\s+\d+).*$', r'\1', command)
            if re.match(r'^.*(-ar\s+\d+).*$', f):
                command = command.replace(f, '').strip()
            if text != 'No Change':
                command += ' -ar {0}'.format(text)

        elif widget == 'audio_bitrate':
            text = self.audio_bitrateComboBox.currentText()
            f = re.sub(r'^.*(-ab\s+\d+k).*$', r'\1', command)
            if re.match(r'^.*(-ab\s+\d+k).*$', f):
                command = command.replace(f, '').strip()
            if text != 'No Change':
                command += ' -ab {0}k'.format(text)

        elif widget in ('channels1', 'channels2'):
            text = self.chan1RadioButton.text() if widget == 'channels1' \
                                            else self.chan2RadioButton.text()
            f = re.sub(r'^.*(-ac\s+\d+).*$', r'\1', command)
            if re.match(r'^.*(-ac\s+\d+).*$', f):
                command = command.replace(f, '').strip()
            command += ' -ac {0}'.format(text)

        self.commandLineEdit.clear()
        self.commandLineEdit.setText(self.remove_consecutive_spaces(command))
Exemplo n.º 23
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES,
                ControlledAttributesDict(self))
        self._guiElements = []  # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        #if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()  # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled else Qt.Dialog
                | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self,
                                         orientation="horizontal",
                                         margin=0,
                                         spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored,
                                       QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal",
                                           margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical",
                                            margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(
                                              QSizePolicy.Expanding,
                                              QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical",
                                             margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal",
                                                  margin=4)
            self.graphButton = gui.button(graphButtonBackground, self,
                                          "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-red.png"))

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning),
                    ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or \
                (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning,
                                      self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = -self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    # ##############################################
    """
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." %
                             str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1
    """

    def restoreWidgetPosition(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored:
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before showEvent and we must not overwrite the the savedGeometry
        # with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            # Restore saved geometry on show
            self.restoreWidgetPosition()
        self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError(
                '{} is not a valid output signal for widget {}'.format(
                    signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activate_loaded_settings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn(
                    "progressBarSet() called without a "
                    "preceding progressBarInit()",
                    stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(
                self.captionTitle +
                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float,
                                    fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(highest_type,
                                     msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(highest_type, tooltip_lines[0],
                                     tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {
            "Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
            "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
            "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)
        }
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".format(
                background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {
            "Info": "canvasIcons:information.png",
            "Warning": "canvasIcons:warning.png",
            "Error": "canvasIcons:error.png"
        }
        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconPath[what], "\n".join(
                                 self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath(
                "canvasIcons",
                os.path.join(environ.canvas_install_dir, "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
            lambda self: self.showMaximized
            if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
            lambda self: self.setVisible(not self.isVisible())
        }

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Exemplo n.º 24
0
class WRepositories(QWidget, Logger.ClassLogger):
    """
    Repositories widget
    """
    def __init__(self, parent=None):
        """
        Constructs WRepositories widget

        @param parent: 
        @type parent:
        """
        QWidget.__init__(self, parent)

        self.localRepoPath = Settings.instance().readValue(
            key='Repositories/local-repo')
        self.localRepository = None
        self.localSaveOpen = None
        self.remoteRepository = None
        self.adaptersRemoteRepository = None

        self.createWidgets()
        self.createConnections()
        self.onResetRemote()

    def localRepoIsPresent(self):
        """
        The local repository is configured ?
        """
        if self.localRepoPath != "Undefined":
            if not os.path.exists(self.localRepoPath):
                self.localConfigured = "Undefined"
                self.testsTab.setTabEnabled(TAB_LOCAL_POS, False)
            else:
                self.localConfigured = self.localRepoPath
                self.testsTab.setTabEnabled(TAB_LOCAL_POS, True)
        else:
            self.localConfigured = self.localRepoPath
            self.testsTab.setTabEnabled(TAB_LOCAL_POS, False)

    def createWidgets(self):
        """
        Create qt widgets

        QTabWidget:
          ________  ________
         /        \/        \___________
        |                               |
        |                               |
        |                               |
        |_______________________________|
        """
        self.line = QFrame()
        self.line.setGeometry(QRect(110, 221, 51, 20))
        self.line.setFrameShape(QFrame.HLine)
        self.line.setFrameShadow(QFrame.Sunken)

        self.testsTab = QTabWidget()
        self.testsTab.setTabPosition(QTabWidget.North)
        self.testsTab.setStyleSheet(
            "QTabWidget { border: 0px; }")  # remove 3D border

        # local repo
        self.localConfigured = self.localRepoPath
        self.localRepository = LocalRepository.Repository(parent=self)
        self.localRepository.deactive()
        self.localRepoIsPresent()

        self.localSaveOpen = LocalRepository.SaveOpenToRepoDialog(parent=self)

        # remote repo
        self.remoteRepository = RemoteTests.Repository(parent=self,
                                                       projectSupport=True)
        if not RCI.instance().isAuthenticated():
            self.remoteRepository.setNotConnected()
            self.remoteRepository.setEnabled(False)

        # adapters remote repo
        self.adaptersRemoteRepository = RemoteAdapters.Repository(parent=self)
        if not RCI.instance().isAuthenticated():
            self.adaptersRemoteRepository.setNotConnected()
            self.adaptersRemoteRepository.setEnabled(False)

        # libraries adapters remote repo
        self.librariesRemoteRepository = RemoteLibraries.Repository(
            parent=self)
        if not RCI.instance().isAuthenticated():
            self.librariesRemoteRepository.setNotConnected()
            self.librariesRemoteRepository.setEnabled(False)

        self.testsTab.addTab(self.localRepository,
                             QIcon(":/repository-tests.png"),
                             self.tr("Local Tests"))
        self.testsTab.addTab(self.remoteRepository,
                             QIcon(":/repository-tests.png"),
                             self.tr("Remote Tests"))
        self.testsTab.setTabEnabled(TAB_LOCAL_POS, False)
        self.testsTab.setTabEnabled(TAB_REMOTE_POS, False)

        self.connectorsTab = QTabWidget()
        self.connectorsTab.setTabPosition(QTabWidget.North)
        self.connectorsTab.setStyleSheet(
            "QTabWidget { border: 0px; }")  # remove 3D border

        self.connectorsTab.addTab(self.adaptersRemoteRepository,
                                  QIcon(":/repository-adapters.png"),
                                  self.tr("Adapters"))
        self.connectorsTab.addTab(self.librariesRemoteRepository,
                                  QIcon(":/repository-libraries.png"),
                                  self.tr("Libraries"))
        self.connectorsTab.setTabEnabled(TAB_ADAPTER_POS, False)
        self.connectorsTab.setTabEnabled(TAB_LIBRARY_POS, False)

        self.title = QLabel(self.tr("Repositories"))
        font = QFont()
        font.setBold(True)
        self.title.setFont(font)

        self.labelHelp = QLabel(
            self.tr(
                "Drag and drop your selected file in the workspace to open it."
            ))
        self.labelHelp.setEnabled(False)
        font = QFont()
        font.setItalic(True)
        self.labelHelp.setFont(font)

        layout = QVBoxLayout()
        layout.addWidget(self.title)
        layout.addWidget(self.labelHelp)

        self.mainTab = QTabWidget()
        self.mainTab.setEnabled(False)
        self.mainTab.setTabPosition(QTabWidget.North)
        self.mainTab.setMinimumWidth(300)

        self.mainTab.addTab(self.testsTab, QIcon(":/test-description.png"),
                            "Tests Listing")
        self.mainTab.addTab(self.connectorsTab, QIcon(":/adapters.png"),
                            "Modules Listing")

        layout.addWidget(self.mainTab)
        layout.addWidget(self.line)

        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        # set the default tab
        defaultTab = Settings.instance().readValue(
            key='Repositories/default-repo-test')
        if int(defaultTab) == TAB_LOCAL_POS or int(
                defaultTab) == TAB_REMOTE_POS:
            self.mainTab.setCurrentIndex(MAIN_TAB_TEST)
            self.testsTab.setCurrentIndex(int(defaultTab))
        else:
            self.mainTab.setCurrentIndex(MAIN_TAB_DEV)
            self.connectorsTab.setCurrentIndex(int(defaultTab) - 2)

    def hideWidgetsHeader(self):
        """
        Hide the widget header
        """
        self.line.hide()
        self.title.hide()
        self.labelHelp.hide()

    def showWidgetsHeader(self):
        """
        Show the widget header
        """
        self.line.show()
        self.title.show()
        self.labelHelp.show()

    def createConnections(self):
        """
        Create qt connections
        """
        pass

    def initLocalRepo(self):
        """
        Initialize the local repository
        """
        if self.localConfigured != "Undefined":
            self.localRepository.active()

    def cleanLocalRepo(self):
        """
        Cleanup the local repository
        """
        self.localRepository.deactive()

    def local(self):
        """
        Returns the local repository widget

        @return: LocalRepository.Repository
        @rtype: QWidget
        """
        return self.localRepository

    def localDialogSave(self):
        """
        Return the save/open dialog
        """
        return self.localSaveOpen

    def remote(self):
        """
        Returns the remote repository widget

        @return: RemoteRepository.Repository
        @rtype: QWidget
        """
        return self.remoteRepository

    def remoteAdapter(self):
        """
        Returns the remote repository widget

        @return: RemoteRepository.Repository
        @rtype: QWidget
        """
        return self.adaptersRemoteRepository

    def remoteLibrary(self):
        """
        Returns the remote repository widget

        @return: RemoteRepository.Repository
        @rtype: QWidget
        """
        return self.librariesRemoteRepository

    def decodeData(self, b64data):
        """
        Decode data
        """
        data_json = ''
        try:
            data_decoded = base64.b64decode(b64data)
        except Exception as e:
            self.error('unable to decode from base64 structure: %s' % str(e))
        else:
            try:
                data_uncompressed = zlib.decompress(data_decoded)
            except Exception as e:
                self.error('unable to decompress: %s' % str(e))
            else:
                try:
                    if sys.version_info > (3, ):
                        data_json = json.loads(
                            data_uncompressed.decode('utf-8'))
                    else:
                        data_json = json.loads(data_uncompressed)
                except Exception as e:
                    self.error('unable to decode from json structure: %s' %
                               str(e))
        return data_json

    # functions for remote repository
    def onLoadRemote(self, data):
        """
        Called on load remote actions
        """
        self.mainTab.setEnabled(True)
        self.labelHelp.setEnabled(True)
        # if UCI.RIGHTS_TESTER in RCI.instance().userRights:
        # self.remoteRepository.setConnected()
        # self.remoteRepository.setEnabled(True)
        # self.localRepository.setEnabled(True)

        # self.testsTab.setTabEnabled( TAB_REMOTE_POS, True )
        # if self.localConfigured != "Undefined":
        # self.testsTab.setTabEnabled( TAB_LOCAL_POS, True )
        # else:
        # self.testsTab.setTabEnabled( TAB_LOCAL_POS, False )
        # self.connectorsTab.setTabEnabled( TAB_LIBRARY_POS, False )
        # self.connectorsTab.setTabEnabled( TAB_ADAPTER_POS, False )

        # defaultTab = Settings.instance().readValue( key = 'Repositories/default-repo-test' )
        # if int(defaultTab) == TAB_LOCAL_POS or int(defaultTab) == TAB_REMOTE_POS:
        # self.mainTab.setCurrentIndex(MAIN_TAB_TEST)
        # self.testsTab.setCurrentIndex(int(defaultTab))
        # else:
        # self.mainTab.setCurrentIndex(MAIN_TAB_DEV)
        # self.connectorsTab.setCurrentIndex(int(defaultTab)-2)

        # self.remoteRepository.defaultActions()
        # self.remoteRepository.initialize(listing= data['repo']  )
        # self.remoteRepository.initializeProjects( projects=data['projects'],
        # defaultProject=data['default-project'] )

        # if UCI.RIGHTS_TESTER in RCI.instance().userRights:
        # self.connectorsTab.setTabEnabled( TAB_ADAPTER_POS, True )
        # self.connectorsTab.setTabEnabled( TAB_LIBRARY_POS, True )

        # if UCI.RIGHTS_TESTER in RCI.instance().userRights: # exception if the developer if also a tester
        # defaultTab = Settings.instance().readValue( key = 'Repositories/default-repo-test' )
        # if int(defaultTab) == TAB_LOCAL_POS or int(defaultTab) == TAB_REMOTE_POS:
        # self.mainTab.setCurrentIndex(MAIN_TAB_TEST)
        # self.testsTab.setCurrentIndex(int(defaultTab))
        # else:
        # self.mainTab.setCurrentIndex(MAIN_TAB_DEV)
        # self.connectorsTab.setCurrentIndex(int(defaultTab)-2)
        # else:
        # defaultTab = Settings.instance().readValue( key = 'Repositories/default-repo-dev' )
        # if int(defaultTab) == TAB_LOCAL_POS or int(defaultTab) == TAB_REMOTE_POS:
        # self.mainTab.setCurrentIndex(MAIN_TAB_TEST)
        # self.testsTab.setCurrentIndex(int(defaultTab))
        # else:
        # self.mainTab.setCurrentIndex(MAIN_TAB_DEV)
        # self.connectorsTab.setCurrentIndex(int(defaultTab)-2)

        # self.adaptersRemoteRepository.setConnected()
        # self.adaptersRemoteRepository.setEnabled(True)
        # self.adaptersRemoteRepository.defaultActions()
        # self.adaptersRemoteRepository.initialize(listing=data['repo-adp'] )

        # self.librariesRemoteRepository.setConnected()
        # self.librariesRemoteRepository.setEnabled(True)
        # self.librariesRemoteRepository.defaultActions()
        # self.librariesRemoteRepository.initialize( listing=data['repo-lib-adp'] )

        # if UCI.RIGHTS_ADMIN in RCI.instance().userRights:
        self.remoteRepository.setConnected()
        self.remoteRepository.setEnabled(True)
        self.localRepository.setEnabled(True)

        self.testsTab.setTabEnabled(TAB_REMOTE_POS, True)
        if self.localConfigured != "Undefined":
            self.testsTab.setTabEnabled(TAB_LOCAL_POS, True)
        else:
            self.testsTab.setTabEnabled(TAB_LOCAL_POS, False)
        self.connectorsTab.setTabEnabled(TAB_ADAPTER_POS, True)
        self.connectorsTab.setTabEnabled(TAB_LIBRARY_POS, True)

        defaultTab = Settings.instance().readValue(
            key='Repositories/default-repo-test')
        if int(defaultTab) == TAB_LOCAL_POS or int(
                defaultTab) == TAB_REMOTE_POS:
            self.mainTab.setCurrentIndex(MAIN_TAB_TEST)
            self.testsTab.setCurrentIndex(int(defaultTab))
        else:
            self.mainTab.setCurrentIndex(MAIN_TAB_DEV)
            self.connectorsTab.setCurrentIndex(int(defaultTab) - 2)

        self.remoteRepository.defaultActions()
        if self.remoteRepository.initializeProjects(
                projects=data['projects'],
                defaultProject=data['default-project']):
            self.remoteRepository.initialize(listing=data['repo'])

        self.adaptersRemoteRepository.setConnected()
        self.adaptersRemoteRepository.setEnabled(True)
        self.adaptersRemoteRepository.defaultActions()
        self.adaptersRemoteRepository.initialize(listing=data['repo-adp'])

        self.librariesRemoteRepository.setConnected()
        self.librariesRemoteRepository.setEnabled(True)
        self.librariesRemoteRepository.defaultActions()
        self.librariesRemoteRepository.initialize(listing=data['repo-lib-adp'])

    def onResetRemote(self):
        """
        Called on reset remote actions
        """
        self.mainTab.setEnabled(False)
        self.labelHelp.setEnabled(False)

        self.remoteRepository.setNotConnected()
        self.remoteRepository.wrepository.clear()
        self.remoteRepository.setEnabled(False)
        self.localRepository.setEnabled(False)
        self.localRepository.defaultActions()

        self.adaptersRemoteRepository.setNotConnected()
        self.adaptersRemoteRepository.wrepository.clear()
        self.adaptersRemoteRepository.setEnabled(False)

        self.librariesRemoteRepository.setNotConnected()
        self.librariesRemoteRepository.wrepository.clear()
        self.librariesRemoteRepository.setEnabled(False)

        self.testsTab.setTabEnabled(TAB_REMOTE_POS, False)
        self.connectorsTab.setTabEnabled(TAB_ADAPTER_POS, False)
        self.testsTab.setTabEnabled(TAB_LOCAL_POS, False)
        self.connectorsTab.setTabEnabled(TAB_LIBRARY_POS, False)

        defaultTab = Settings.instance().readValue(
            key='Repositories/default-repo-test')
        if int(defaultTab) == TAB_LOCAL_POS or int(
                defaultTab) == TAB_REMOTE_POS:
            self.mainTab.setCurrentIndex(MAIN_TAB_TEST)
            self.testsTab.setCurrentIndex(int(defaultTab))
        else:
            self.mainTab.setCurrentIndex(MAIN_TAB_DEV)
            self.connectorsTab.setCurrentIndex(int(defaultTab) - 2)

    def onRefreshRemoteTests(self, data, projectId, forSaveAs=False):
        """
        """
        self.remoteRepository.defaultActions()

        if forSaveAs:
            self.remoteRepository.initializeSaveAs(listing=data,
                                                   reloadItems=True)
        else:
            # update default project
            projectName = self.remoteRepository.getProjectName(projectId)
            self.remoteRepository.setDefaultProject(projectName=projectName)

            # reconstruct
            self.remoteRepository.initialize(listing=data)

    def onRefreshRemoteAdapters(self, data):
        """
        """
        self.adaptersRemoteRepository.defaultActions()
        self.adaptersRemoteRepository.initialize(listing=data)

    def onRefreshRemoteLibraries(self, data):
        """
        """
        self.librariesRemoteRepository.defaultActions()
        self.librariesRemoteRepository.initialize(listing=data)