class MainWindow(QMainWindow): """The application's main window """ # Emitted when there are pending files to be processed new_pending_files = QtCore.Signal() def __init__(self, app): super(MainWindow, self).__init__() # Window layout - a splitter with the image on the left and controls # on the right self._image_widget = ImageLabel(self) self._controls = Controls(self) self._splitter = QSplitter() self._splitter.addWidget(self._image_widget) self._splitter.addWidget(self._controls) self._splitter.setSizes([1200, 600]) # Main window layout self.setCentralWidget(self._splitter) # Connect controls to handlers self._controls.ok.clicked.connect(self.ok) self._controls.cancel.clicked.connect(self.cancel) self._controls.inbox.choose_directory.clicked.connect( self.choose_inbox) self._controls.processed.choose_directory.clicked.connect( self.choose_processed) # Directories mydocuments = QDesktopServices.storageLocation( QDesktopServices.DocumentsLocation) self._inbox = Path(QSettings().value('inbox', str(Path(mydocuments) / 'inbox'))) self._processed = Path(QSettings().value( 'processed', str(Path(mydocuments) / 'processed'))) self._controls.inbox.set_link(str(self._inbox.as_uri()), self._inbox.name) self._controls.processed.set_link(str(self._processed.as_uri()), self._processed.name) # A stack of Path objects to be processed self._pending_files = [] # The Path currently shown in the UI self._under_review = None # Watch the inbox directory, if it exists self.new_pending_files.connect(self.process_next_pending, QtCore.Qt.QueuedConnection) if self._inbox.is_dir(): self._watcher = NewFileWatcher(self._inbox, IMAGE_SUFFIXES_RE) self._watcher.new_file.connect(self.new_image_file) else: self._watcher = None self.empty_controls() # Setup drag-drop handling self.setAcceptDrops(True) self._controls.installEventFilter(self) self._splitter.installEventFilter(self) def new_inbox_directory(self): """Watch the inbox directory """ print('MainWindow.new_inbox_directory [{0}]'.format(self._inbox)) if self._watcher: self._watcher.new_file.disconnect() self._watcher = NewFileWatcher(self._inbox, IMAGE_SUFFIXES_RE) self._watcher.new_file.connect(self.new_image_file) def new_image_file(self, path): """Slot for self._watcher.new_file """ print('MainWindow.new_image_file [{0}]'.format(path)) self._pending_files.append(path) self.new_pending_files.emit() @report_to_user def process_next_pending(self): """Loads the next pending image for review """ print('MainWindow.process_next_pending: [{0}] files'.format( len(self._pending_files))) if not self._under_review: if self._pending_files: self.review_image(self._pending_files.pop()) else: self.empty_controls() def review_image(self, path): """Loads path for review """ print('MainWindow.review_image [{0}]'.format(path)) # Arbitrary delay to give the capture software time to finish writing # the image. time.sleep(1) image = cv2.imread(str(path)) if image is None: raise ValueError('Unable to read [{0}]'.format(path)) else: self._under_review = path self.setWindowTitle('') self.setWindowFilePath(str(path)) self._controls.specimen.setText(QSettings().value('specimen')) self._controls.location.setText(QSettings().value('location')) self._image_widget.set_pixmap( QPixmap.fromImage(qimage_of_bgr(image))) self._controls.image_handling.setEnabled(True) def empty_controls(self): """Clears controls """ print('MainWindow.empty_controls') self._under_review = None self.setWindowTitle('Syrup') self.setWindowFilePath(None) self._image_widget.set_pixmap(None) self._controls.clear() self._controls.image_handling.setEnabled(False) @report_to_user def ok(self): print('MainWindow.ok') specimen = self._controls.specimen.text() location = self._controls.location.text() if not SPECIMEN_RE.match(specimen): raise ValueError( 'Please enter nine digits for the specimen barcode') elif not LOCATION_RE.match(location): raise ValueError( 'Please enter a letter "L" and nine digits for the ' 'location barcode') else: if not self._processed.is_dir(): self._processed.mkdir(parents=True) destination = self._processed / '{0}_{1}'.format( specimen, location) destination = destination.with_suffix(self._under_review.suffix) move_and_rename(self._under_review, destination) QSettings().setValue('specimen', specimen) QSettings().setValue('location', location) self._under_review = None self.process_next_pending() @report_to_user def cancel(self): """Closes the image under review without moving the image file """ print('MainWindow.cancel') self._under_review = None self.process_next_pending() @report_to_user def choose_inbox(self): """Prompts the user to choose the inbox directory """ directory = QFileDialog.getExistingDirectory( self, "Choose the inbox directory", str(self._inbox)) if directory: directory = Path(directory) if directory == self._processed: raise ValueError('The inbox directory cannot be the same as ' 'the processed directory') else: self._inbox = directory print('New inbox directory [{0}]'.format(self._inbox)) self._controls.inbox.set_link(str(self._inbox.as_uri()), self._inbox.name) QSettings().setValue('inbox', str(self._inbox)) self.new_inbox_directory() @report_to_user def choose_processed(self): """Prompts the user to choose the processed directory """ directory = QFileDialog.getExistingDirectory( self, "Choose the processed directory", str(self._processed)) if directory: directory = Path(directory) if directory == self._inbox: raise ValueError('The inbox directory cannot be the same as ' 'the processed directory') else: self._processed = directory print('New processed directory [{0}]'.format(self._processed)) self._controls.processed.set_link( str(self._processed.as_uri()), self._processed.name) QSettings().setValue('processed', str(self._processed)) def write_geometry_settings(self): "Writes geometry to settings" print('MainWindow.write_geometry_settings') # Taken from http://stackoverflow.com/a/8736705 # TODO LH Test on multiple display system s = QSettings() s.setValue("mainwindow/geometry", self.saveGeometry()) s.setValue("mainwindow/pos", self.pos()) s.setValue("mainwindow/size", self.size()) def show_from_geometry_settings(self): print('MainWindow.show_from_geometry_settings') # TODO LH What if screen resolution, desktop config change or roaming # profile means that restored state is outside desktop? s = QSettings() self.restoreGeometry( s.value("mainwindow/geometry", self.saveGeometry())) if not (self.isMaximized() or self.isFullScreen()): self.move(s.value("mainwindow/pos", self.pos())) self.resize(s.value("mainwindow/size", self.size())) self.show() def closeEvent(self, event): """QWidget virtual """ print('MainWindow.closeEvent') self.write_geometry_settings() event.accept() def eventFilter(self, obj, event): "Event filter that accepts drag-drop events" if event.type() in (QEvent.DragEnter, QEvent.Drop): return True else: return super(MainWindow, self).eventFilter(obj, event) def _accept_drag_drop(self, event): """If no image is under review and event refers to a single image file, returns the path. Returns None otherwise. """ if self._under_review: return None else: urls = event.mimeData().urls() if event.mimeData() else None path = Path( urls[0].toLocalFile()) if urls and 1 == len(urls) else None print(path, IMAGE_SUFFIXES_RE.match(path.suffix)) if path and IMAGE_SUFFIXES_RE.match(path.suffix): return urls[0].toLocalFile() else: return None def dragEnterEvent(self, event): """QWidget virtual """ print('MainWindow.dragEnterEvent') if self._accept_drag_drop(event): event.acceptProposedAction() else: super(MainWindow, self).dragEnterEvent(event) @report_to_user def dropEvent(self, event): """QWidget virtual """ print('MainWindow.dropEvent') res = self._accept_drag_drop(event) if res: event.acceptProposedAction() self.review_image(Path(res)) else: super(MainWindow, self).dropEvent(event)
class Window(QWidget): def init(self, self_mysql): self.ref = False self.ndb = False self.Data_Name = None self.table_Name = None self.mysql = self_mysql self.table = Table() self.tree = treeWidget(self.mysql) text = ( "<center><h1 style=';color:Lightblue;'>MySQL<small style='color:#0099CC;'> Control Panel</small></h1></center>" ) self.vbox = QVBoxLayout(self) self.split = QSplitter() self.tree.setHidden(True) self.tree.itemClicked.connect(self.setInTable) self.tree.itemExpanded.connect(self.expand) self.tree.itemCollapsed.connect(self.collaps) self.split.addWidget(self.tree) self.split.addWidget(self.table) self.split.setSizes([60, 400]) self.vbox.addWidget(label(text)) self.vbox.addWidget(self.split) self.vbox.setContentsMargins(-18, -18, -18, -18) def expand(self, it): if it.isExpanded(): if not it.text(0) in self.tree.li: self.tree.li.append(it.text(0)) def collaps(self, it): if it.text(0) in self.tree.li: self.tree.li.remove(it.text(0)) # set Name 'database & table' in TreeList # def con(self): self.tree.clear() if self.tree.isHidden: self.tree.setHidden(False) self.tree.headerItem().setText(0, self.Data_Name) self.tree.add_to_tree() self.ref = True def setInTable(self, nt): if not str(nt).startswith('<'): table = nt self.table_Name = nt else: if nt.statusTip(0) != 'main': self.tree.headerItem().setText(0, nt.statusTip(0)) self.ndb = nt.statusTip(0) self.Data_Name = nt.statusTip(0) self.table_Name = nt.text(0) else: self.tree.headerItem().setText(0, nt.text(0)) self.Data_Name = nt.text(0) self.table_Name = None if self.ndb: if self.table_Name in self.mysql.getTables(self.Data_Name): self.setDataInTable(self.table_Name) self.ref = True else: pass # Set Data Table In TableWidget # def setDataInTable(self, tab): Icol = 0 if self.ndb: data_dic = self.mysql.dataCol(self.ndb, tab) cols = self.mysql.getColumns(self.ndb, tab) self.table.setColumnCount(len(cols)) if data_dic != None: self.table.setRowCount(int(data_dic[1])) for col in cols: item = QTableWidgetItem self.table.setHorizontalHeaderItem(Icol, item(col)) Irow = 0 for row in data_dic[0][col]: self.item = QTableWidgetItem(str(row)) self.item.setFont(QFont("andalus", 12)) self.table.setItem(Irow, Icol, self.item) Irow += 1 Icol += 1
def __init__(self, *args, **kwargs): QMainWindow.__init__(self, *args, **kwargs) self.installEventFilter(self) #self.setWindowFlags( QtCore.Qt.Drawer ) self.setWindowTitle(Window_global.title) verticalSplitter = QSplitter(QtCore.Qt.Vertical) self.setCentralWidget(verticalSplitter) horizonSplitter1 = QSplitter(QtCore.Qt.Horizontal) horizonSplitter2 = QSplitter(QtCore.Qt.Horizontal) verticalSplitter.addWidget(horizonSplitter1) verticalSplitter.addWidget(horizonSplitter2) widgetSelArea = QWidget() layoutSelArea = QVBoxLayout(widgetSelArea) labelSelTextList = QLabel('Images from Selection') selTextureList = QListWidget() selTextureList.setSelectionMode(QAbstractItemView.ExtendedSelection) layoutSelArea.addWidget(labelSelTextList) layoutSelArea.addWidget(selTextureList) imageBaseSelArea = ImageBase() horizonSplitter1.addWidget(widgetSelArea) horizonSplitter1.addWidget(imageBaseSelArea) widgetPathArea = QWidget() layoutPathArea = QVBoxLayout(widgetPathArea) layoutAddTab = QHBoxLayout() removeTabButton = QPushButton('Remove Tab') addTabButton = QPushButton('Add Tab') self.tabWidget = Tab() buttonLayout = QHBoxLayout() getImageButton = QPushButton('Get Image') removeImageButton = QPushButton('Remove Image') layoutPathArea.addLayout(layoutAddTab) layoutPathArea.addWidget(self.tabWidget) layoutPathArea.addLayout(buttonLayout) imageBasePathArea = ImageBase() layoutAddTab.addWidget(removeTabButton) layoutAddTab.addWidget(addTabButton) buttonLayout.addWidget(getImageButton) buttonLayout.addWidget(removeImageButton) horizonSplitter2.addWidget(widgetPathArea) horizonSplitter2.addWidget(imageBasePathArea) Window_global.selTextureList = selTextureList Window_global.imageBaseSelArea = imageBaseSelArea Window_global.imageBasePathArea = imageBasePathArea Window_global.verticalSplitter = verticalSplitter Window_global.horizonSplitter1 = horizonSplitter1 Window_global.horizonSplitter2 = horizonSplitter2 Window_global.getImageButton = getImageButton Window_global.removeImageButton = removeImageButton Window_global.tabWidget = self.tabWidget Window_global.tabWidget.addTab('newTab') verticalSplitter.setSizes([100, 100]) horizonSplitter1.setSizes([100, 100]) horizonSplitter2.setSizes([100, 100]) Window_global.loadInfo() try: Window_global.loadInfo2() except: pass Functions.updateSelTextureList() QtCore.QObject.connect(addTabButton, QtCore.SIGNAL('clicked()'), self.addTab) QtCore.QObject.connect(removeTabButton, QtCore.SIGNAL('clicked()'), self.removeTab) QtCore.QObject.connect(Window_global.selTextureList, QtCore.SIGNAL('itemSelectionChanged()'), Functions.loadImageSelArea) QtCore.QObject.connect(Window_global.horizonSplitter1, QtCore.SIGNAL('splitterMoved(int,int)'), Functions.splitterMoved1) QtCore.QObject.connect(Window_global.horizonSplitter2, QtCore.SIGNAL('splitterMoved(int,int)'), Functions.splitterMoved2) QtCore.QObject.connect(getImageButton, QtCore.SIGNAL('clicked()'), Functions.getImage) QtCore.QObject.connect(removeImageButton, QtCore.SIGNAL('clicked()'), Functions.removeImage) imageBaseSelArea.clear() imageBasePathArea.clear() getImageButton.setEnabled(False) removeImageButton.setEnabled(False)
def __init__(self, *args, **kwargs ): QMainWindow.__init__( self, *args, **kwargs ) self.installEventFilter( self ) #self.setWindowFlags( QtCore.Qt.Drawer ) self.setWindowTitle( Window_global.title ) verticalSplitter = QSplitter(QtCore.Qt.Vertical) self.setCentralWidget( verticalSplitter ) horizonSplitter1 = QSplitter(QtCore.Qt.Horizontal) horizonSplitter2 = QSplitter(QtCore.Qt.Horizontal) verticalSplitter.addWidget( horizonSplitter1 ) verticalSplitter.addWidget( horizonSplitter2 ) widgetSelArea = QWidget() layoutSelArea = QVBoxLayout(widgetSelArea) labelSelTextList = QLabel( 'Images from Selection' ) selTextureList = QListWidget() selTextureList.setSelectionMode( QAbstractItemView.ExtendedSelection ) layoutSelArea.addWidget( labelSelTextList ) layoutSelArea.addWidget( selTextureList ) imageBaseSelArea = ImageBase() horizonSplitter1.addWidget( widgetSelArea ) horizonSplitter1.addWidget( imageBaseSelArea ) widgetPathArea = QWidget() layoutPathArea = QVBoxLayout( widgetPathArea ) layoutAddTab = QHBoxLayout() removeTabButton = QPushButton( 'Remove Tab' ) addTabButton = QPushButton( 'Add Tab' ) self.tabWidget = Tab() buttonLayout = QHBoxLayout() getImageButton = QPushButton( 'Get Image' ) removeImageButton = QPushButton( 'Remove Image' ) layoutPathArea.addLayout( layoutAddTab ) layoutPathArea.addWidget( self.tabWidget ) layoutPathArea.addLayout( buttonLayout ) imageBasePathArea = ImageBase() layoutAddTab.addWidget( removeTabButton ) layoutAddTab.addWidget( addTabButton ) buttonLayout.addWidget( getImageButton ) buttonLayout.addWidget( removeImageButton ) horizonSplitter2.addWidget( widgetPathArea ) horizonSplitter2.addWidget( imageBasePathArea ) Window_global.selTextureList = selTextureList Window_global.imageBaseSelArea = imageBaseSelArea Window_global.imageBasePathArea = imageBasePathArea Window_global.verticalSplitter = verticalSplitter Window_global.horizonSplitter1 = horizonSplitter1 Window_global.horizonSplitter2 = horizonSplitter2 Window_global.getImageButton = getImageButton Window_global.removeImageButton = removeImageButton Window_global.tabWidget = self.tabWidget Window_global.tabWidget.addTab( 'newTab' ) verticalSplitter.setSizes( [100,100] ) horizonSplitter1.setSizes( [100,100] ) horizonSplitter2.setSizes( [100,100] ) Window_global.loadInfo() try:Window_global.loadInfo2() except:pass Functions.updateSelTextureList() QtCore.QObject.connect( addTabButton, QtCore.SIGNAL( 'clicked()' ), self.addTab ) QtCore.QObject.connect( removeTabButton, QtCore.SIGNAL( 'clicked()' ), self.removeTab ) QtCore.QObject.connect( Window_global.selTextureList, QtCore.SIGNAL( 'itemSelectionChanged()' ), Functions.loadImageSelArea ) QtCore.QObject.connect( Window_global.horizonSplitter1, QtCore.SIGNAL( 'splitterMoved(int,int)' ), Functions.splitterMoved1 ) QtCore.QObject.connect( Window_global.horizonSplitter2, QtCore.SIGNAL( 'splitterMoved(int,int)' ), Functions.splitterMoved2 ) QtCore.QObject.connect( getImageButton, QtCore.SIGNAL( 'clicked()' ), Functions.getImage ) QtCore.QObject.connect( removeImageButton, QtCore.SIGNAL( 'clicked()' ), Functions.removeImage ) imageBaseSelArea.clear() imageBasePathArea.clear() getImageButton.setEnabled( False ) removeImageButton.setEnabled( False )
class MainWindow(QMainWindow): def __init__(self, datta): QMainWindow.__init__(self) self.setWindowTitle('Project Parser') appIcon = QIcon('search.png') self.setWindowIcon(appIcon) self.viewPortBL = QDesktopWidget().availableGeometry().topLeft() self.viewPortTR = QDesktopWidget().availableGeometry().bottomRight() self.margin = int(QDesktopWidget().availableGeometry().width()*0.1/2) self.shirina = QDesktopWidget().availableGeometry().width() - self.margin*2 self.visota = QDesktopWidget().availableGeometry().height() - self.margin*2 self.setGeometry(self.viewPortBL.x() + self.margin, self.viewPortBL.y() + self.margin, self.shirina, self.visota) # statusbar self.myStatusBar = QStatusBar() self.setStatusBar(self.myStatusBar) #lower long layout self.lowerLong = QFrame() self.detailsLabel = QLabel() self.skillsLabel = QLabel() self.urlLabel = QLabel() self.locationLabel = QLabel() self.skillsLabel.setText('skills') self.detailsLabel.setWordWrap(True) self.la = QVBoxLayout() self.la.addWidget(self.detailsLabel) self.la.addWidget(self.skillsLabel) self.la.addWidget(self.urlLabel) self.la.addWidget(self.locationLabel) self.lowerLong.setLayout(self.la) # table self.source_model = MyTableModel(self, datta, ['Id', 'Date', 'Title']) self.proxy_model = myTableProxy(self) self.proxy_model.setSourceModel(self.source_model) self.proxy_model.setDynamicSortFilter(True) self.table_view = QTableView() self.table_view.setModel(self.proxy_model) self.table_view.setAlternatingRowColors(True) self.table_view.resizeColumnsToContents() self.table_view.resizeRowsToContents() self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.setSortingEnabled(True) self.table_view.sortByColumn(2, Qt.AscendingOrder) # events self.selection = self.table_view.selectionModel() self.selection.selectionChanged.connect(self.handleSelectionChanged) #DO NOT use CreateIndex() method, use index() index = self.proxy_model.index(0,0) self.selection.select(index, QItemSelectionModel.Select) self.upperLong = self.table_view # right side widgets self.right = QFrame() self.la1 = QVBoxLayout() self.btnDownload = QPushButton('Download data') self.btnDownload.clicked.connect(self.download) self.myButton = QPushButton('Show Skillls') self.myButton.clicked.connect(self.showAllSkills) self.btnSearchByWord = QPushButton('Search by word(s)') self.btnSearchByWord.clicked.connect(self.onSearchByWord) self.btnResetFilter= QPushButton('Discard Filter') self.btnResetFilter.clicked.connect(self.discardFilter) self.btnCopyURL = QPushButton('URL to Clipboard') self.btnCopyURL.clicked.connect(self.copyToClipboard) self.btnExit = QPushButton('Exit') self.btnExit.clicked.connect(lambda: sys.exit()) self.dateTimeStamp = QLabel() self.la1.addWidget(self.btnDownload) self.la1.addSpacing(10) self.la1.addWidget(self.myButton) self.la1.addSpacing(10) self.la1.addWidget(self.btnSearchByWord) self.la1.addSpacing(10) self.la1.addWidget(self.btnResetFilter) self.la1.addSpacing(10) self.la1.addWidget(self.btnCopyURL) self.la1.addSpacing(70) self.la1.addWidget(self.btnExit) self.la1.addStretch(stretch=0) self.la1.addWidget(self.dateTimeStamp) self.right.setLayout(self.la1) self.right.setFrameShape(QFrame.StyledPanel) # splitters self.horiSplit = QSplitter(Qt.Vertical) self.horiSplit.addWidget(self.upperLong) self.horiSplit.addWidget(self.lowerLong) self.horiSplit.setSizes([self.visota/2, self.visota/2]) self.vertiSplit = QSplitter(Qt.Horizontal) self.vertiSplit.addWidget(self.horiSplit) self.vertiSplit.addWidget(self.right) self.vertiSplit.setSizes([self.shirina*3/4, self.shirina*1/4]) self.setCentralWidget(self.vertiSplit) self.settings = QSettings('elance.ini', QSettings.IniFormat) self.settings.beginGroup('DATE_STAMP') self.dateTimeStamp.setText('Data actuality: %s' % self.settings.value('date/time')) self.settings.endGroup() self.statusText = '' def handleSelectionChanged(self, selected, deselected): for index in selected.first().indexes(): #print('Row %d is selected' % index.row()) ind = index.model().mapToSource(index) desc = ind.model().mylist[ind.row()]['Description'] self.detailsLabel.setText(desc) skills = ', '.join(ind.model().mylist[ind.row()]['Skills']).strip() self.skillsLabel.setText(skills) url = ind.model().mylist[ind.row()]['URL'] self.urlLabel.setText(url) location = ind.model().mylist[ind.row()]['Location'] self.locationLabel.setText(location) def showAllSkills(self): listSkills = [] for elem in self.source_model.mylist: listSkills += elem['Skills'] allSkills = Counter(listSkills) tbl = MyTableModel(self, allSkills.items(), ['Skill', 'Freq']) win = skillsWindow(tbl, self.table_view) win.exec_() def discardFilter(self): self.table_view.model().emit(SIGNAL("modelAboutToBeReset()")) self.table_view.model().criteria = {} self.table_view.model().emit(SIGNAL("modelReset()")) self.table_view.resizeRowsToContents() def download(self): self.btnDownload.setDisabled(True) self.statusLabel = QLabel('Connecting') self.progressBar = QProgressBar() self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.myStatusBar.addWidget(self.statusLabel, 2) self.myStatusBar.addWidget(self.progressBar, 1) self.progressBar.setValue(1) self.settings.beginGroup('URLS') initialLink = self.settings.value('CategoriesDetailed/VahaSelected/InitialLink') pagingLink = self.settings.value('CategoriesDetailed/VahaSelected/PagingLink') self.settings.endGroup() downloader = Downloader(initialLink, pagingLink, 25, 5) downloader.messenger.downloadProgressChanged.connect(self.onDownloadProgressChanged) downloader.messenger.downloadComplete.connect(self.onDownloadComplete) downloader.download() def onDownloadComplete(self): #QMessageBox.information(self, 'Download complete', 'Download complete!', QMessageBox.Ok) self.table_view.model().emit(SIGNAL("modelAboutToBeReset()")) self.settings.beginGroup('DATE_STAMP') self.settings.setValue('date/time', time.strftime('%d-%b-%Y, %H:%M:%S')) self.dateTimeStamp.setText('Data actuality: %s' % self.settings.value('date/time')) self.settings.endGroup() with open("elance.json") as json_file: jobDB = json.load(json_file) for elem in jobDB: words = nltk.tokenize.regexp_tokenize(elem['Title'].lower(), r'\w+') elem['Tokens'] = words elem['Skills'] = [t.strip() for t in elem['Skills'].split(',')] self.source_model.mylist = jobDB self.table_view.model().emit(SIGNAL("modelReset()")) self.btnDownload.setEnabled(True) self.myStatusBar.removeWidget(self.statusLabel) self.myStatusBar.removeWidget(self.progressBar) self.myStatusBar.showMessage(self.statusText, timeout = 5000) def onDownloadProgressChanged(self, stata): self.progressBar.setValue(stata[2]) #text = 'Processed records{:5d} of{:5d}'.format(percentage[0], percentage[1]) bajtikov = '{:,}'.format(stata[5]) self.statusText = 'Processed page{:4d} of{:4d}. \ Job entries{:5d} of{:5d}. \ Downloaded{:>12s} Bytes'.format(stata[3], stata[4], stata[0], stata[1], bajtikov) self.statusLabel.setText(self.statusText) def copyToClipboard(self): clipboard = QApplication.clipboard() clipboard.setText(self.urlLabel.text()) self.myStatusBar.showMessage(self.urlLabel.text(), timeout = 3000) def onSearchByWord(self): text, ok = QInputDialog.getText(self, 'Search the base by word(s)', 'Enter your keyword/phrase to search for:') if ok: words = [t.strip() for t in nltk.tokenize.regexp_tokenize(text.lower(), r'\w+')] self.table_view.model().emit(SIGNAL("modelAboutToBeReset()")) self.table_view.model().criteria = {'Description' : words} self.table_view.model().emit(SIGNAL("modelReset()"))
class MainWindow(QMainWindow): """The application's main window """ # Emitted when there are pending files to be processed new_pending_files = QtCore.Signal() def __init__(self, app): super(MainWindow, self).__init__() # Window layout - a splitter with the image on the left and controls # on the right self._image_widget = ImageLabel(self) self._controls = Controls(self) self._splitter = QSplitter() self._splitter.addWidget(self._image_widget) self._splitter.addWidget(self._controls) self._splitter.setSizes([1200, 600]) # Main window layout self.setCentralWidget(self._splitter) # Connect controls to handlers self._controls.ok.clicked.connect(self.ok) self._controls.cancel.clicked.connect(self.cancel) self._controls.inbox.choose_directory.clicked.connect(self.choose_inbox) self._controls.processed.choose_directory.clicked.connect(self.choose_processed) # Directories mydocuments = QDesktopServices.storageLocation( QDesktopServices.DocumentsLocation) self._inbox = Path(QSettings().value('inbox', str(Path(mydocuments) / 'inbox'))) self._processed = Path(QSettings().value('processed', str(Path(mydocuments) / 'processed'))) self._controls.inbox.set_link(str(self._inbox.as_uri()), self._inbox.name) self._controls.processed.set_link(str(self._processed.as_uri()), self._processed.name) # A stack of Path objects to be processed self._pending_files = [] # The Path currently shown in the UI self._under_review = None # Watch the inbox directory, if it exists self.new_pending_files.connect(self.process_next_pending, QtCore.Qt.QueuedConnection) if self._inbox.is_dir(): self._watcher = NewFileWatcher(self._inbox, IMAGE_SUFFIXES_RE) self._watcher.new_file.connect(self.new_image_file) else: self._watcher = None self.empty_controls() # Setup drag-drop handling self.setAcceptDrops(True) self._controls.installEventFilter(self) self._splitter.installEventFilter(self) def new_inbox_directory(self): """Watch the inbox directory """ print('MainWindow.new_inbox_directory [{0}]'.format(self._inbox)) if self._watcher: self._watcher.new_file.disconnect() self._watcher = NewFileWatcher(self._inbox, IMAGE_SUFFIXES_RE) self._watcher.new_file.connect(self.new_image_file) def new_image_file(self, path): """Slot for self._watcher.new_file """ print('MainWindow.new_image_file [{0}]'.format(path)) self._pending_files.append(path) self.new_pending_files.emit() @report_to_user def process_next_pending(self): """Loads the next pending image for review """ print('MainWindow.process_next_pending: [{0}] files'.format( len(self._pending_files))) if not self._under_review: if self._pending_files: self.review_image(self._pending_files.pop()) else: self.empty_controls() def review_image(self, path): """Loads path for review """ print('MainWindow.review_image [{0}]'.format(path)) # Arbitrary delay to give the capture software time to finish writing # the image. time.sleep(1) image = cv2.imread(str(path)) if image is None: raise ValueError('Unable to read [{0}]'.format(path)) else: self._under_review = path self.setWindowTitle('') self.setWindowFilePath(str(path)) self._controls.specimen.setText(QSettings().value('specimen')) self._controls.location.setText(QSettings().value('location')) self._image_widget.set_pixmap(QPixmap.fromImage(qimage_of_bgr(image))) self._controls.image_handling.setEnabled(True) def empty_controls(self): """Clears controls """ print('MainWindow.empty_controls') self._under_review = None self.setWindowTitle('Syrup') self.setWindowFilePath(None) self._image_widget.set_pixmap(None) self._controls.clear() self._controls.image_handling.setEnabled(False) @report_to_user def ok(self): print('MainWindow.ok') specimen = self._controls.specimen.text() location = self._controls.location.text() if not SPECIMEN_RE.match(specimen): raise ValueError('Please enter nine digits for the specimen barcode') elif not LOCATION_RE.match(location): raise ValueError('Please enter a letter "L" and nine digits for the ' 'location barcode') else: if not self._processed.is_dir(): self._processed.mkdir(parents=True) destination = self._processed / '{0}_{1}'.format(specimen, location) destination = destination.with_suffix(self._under_review.suffix) move_and_rename(self._under_review, destination) QSettings().setValue('specimen', specimen) QSettings().setValue('location', location) self._under_review = None self.process_next_pending() @report_to_user def cancel(self): """Closes the image under review without moving the image file """ print('MainWindow.cancel') self._under_review = None self.process_next_pending() @report_to_user def choose_inbox(self): """Prompts the user to choose the inbox directory """ directory = QFileDialog.getExistingDirectory(self, "Choose the inbox directory", str(self._inbox)) if directory: directory = Path(directory) if directory == self._processed: raise ValueError('The inbox directory cannot be the same as ' 'the processed directory') else: self._inbox = directory print('New inbox directory [{0}]'.format(self._inbox)) self._controls.inbox.set_link(str(self._inbox.as_uri()), self._inbox.name) QSettings().setValue('inbox', str(self._inbox)) self.new_inbox_directory() @report_to_user def choose_processed(self): """Prompts the user to choose the processed directory """ directory = QFileDialog.getExistingDirectory(self, "Choose the processed directory", str(self._processed)) if directory: directory = Path(directory) if directory == self._inbox: raise ValueError('The inbox directory cannot be the same as ' 'the processed directory') else: self._processed = directory print('New processed directory [{0}]'.format(self._processed)) self._controls.processed.set_link(str(self._processed.as_uri()), self._processed.name) QSettings().setValue('processed', str(self._processed)) def write_geometry_settings(self): "Writes geometry to settings" print('MainWindow.write_geometry_settings') # Taken from http://stackoverflow.com/a/8736705 # TODO LH Test on multiple display system s = QSettings() s.setValue("mainwindow/geometry", self.saveGeometry()) s.setValue("mainwindow/pos", self.pos()) s.setValue("mainwindow/size", self.size()) def show_from_geometry_settings(self): print('MainWindow.show_from_geometry_settings') # TODO LH What if screen resolution, desktop config change or roaming # profile means that restored state is outside desktop? s = QSettings() self.restoreGeometry(s.value("mainwindow/geometry", self.saveGeometry())) if not (self.isMaximized() or self.isFullScreen()): self.move(s.value("mainwindow/pos", self.pos())) self.resize(s.value("mainwindow/size", self.size())) self.show() def closeEvent(self, event): """QWidget virtual """ print('MainWindow.closeEvent') self.write_geometry_settings() event.accept() def eventFilter(self, obj, event): "Event filter that accepts drag-drop events" if event.type() in (QEvent.DragEnter, QEvent.Drop): return True else: return super(MainWindow, self).eventFilter(obj, event) def _accept_drag_drop(self, event): """If no image is under review and event refers to a single image file, returns the path. Returns None otherwise. """ if self._under_review: return None else: urls = event.mimeData().urls() if event.mimeData() else None path = Path(urls[0].toLocalFile()) if urls and 1 == len(urls) else None print(path, IMAGE_SUFFIXES_RE.match(path.suffix)) if path and IMAGE_SUFFIXES_RE.match(path.suffix): return urls[0].toLocalFile() else: return None def dragEnterEvent(self, event): """QWidget virtual """ print('MainWindow.dragEnterEvent') if self._accept_drag_drop(event): event.acceptProposedAction() else: super(MainWindow, self).dragEnterEvent(event) @report_to_user def dropEvent(self, event): """QWidget virtual """ print('MainWindow.dropEvent') res = self._accept_drag_drop(event) if res: event.acceptProposedAction() self.review_image(Path(res)) else: super(MainWindow, self).dropEvent(event)