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
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
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
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
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
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
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
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)
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
def hide(self): if self.do_hide: QFrame.hide(self)
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))
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
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))
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)
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
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
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))
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)
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))
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)
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)