Source code for wbia.gui.newgui

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This should probably be renamed guifront.py This defines all of the visual
components to the GUI It is invoked from guiback, which handles the nonvisual
logic.


BUGS:
    * Copying the ungrouped imageset raises an error. Should have the option
    to copy or move it. Other special imageset should not have this option.

    Should gray out an option if it is not available.


CommandLine:
    wbia --dbdir ~/lev/media/hdd/work/WWF_Lynx/ --name-tab
"""
from __future__ import absolute_import, division, print_function
from six.moves import zip, map, filter
from os.path import isdir
import sys
import functools
import utool as ut
from wbia.guitool.__PYQT__ import QtCore
from wbia.guitool.__PYQT__ import QtWidgets
from wbia.guitool.__PYQT__.QtCore import Qt
from wbia.guitool import slot_, ChangeLayoutContext

# from wbia.guitool import BlockContext
# from wbia.guitool import checks_qt_error
import wbia.guitool as gt
import wbia.plottool as pt
from wbia.plottool import color_funcs
from wbia.gui import guiheaders as gh
from wbia.gui import guimenus
import six
from wbia.viz.interact import interact_annotations2
from wbia.gui.guiheaders import (
    IMAGE_TABLE,
    IMAGE_GRID,
    ANNOTATION_TABLE,
    NAME_TABLE,
    NAMES_TREE,
    IMAGESET_TABLE,
)
from wbia.gui.models_and_views import (
    IBEISStripeModel,
    IBEISTableView,
    IBEISItemModel,
    IBEISTreeView,
    ImagesetTableModel,
    ImagesetTableView,
    IBEISTableWidget,
    IBEISTreeWidget,
    ImagesetTableWidget,
)
from wbia import constants as const

print, rrr, profile = ut.inject2(__name__)


VERBOSE_GUI = ut.VERBOSE or ut.get_argflag(('--verbose-gui', '--verbgui'))
WITH_GUILOG = ut.get_argflag('--guilog')

#############################
###### Tab Widgets #######
#############################


[docs]class APITabWidget(QtWidgets.QTabWidget): """ Holds the table-tabs use setCurrentIndex to change the selection """ def __init__(tabwgt, parent=None, horizontalStretch=1): # QtWidgets.QTabWidget.__init__(tabwgt, parent) super(APITabWidget, tabwgt).__init__(parent) tabwgt.ibswgt = parent tabwgt._sizePolicy = gt.newSizePolicy(tabwgt, horizontalStretch=horizontalStretch) tabwgt.setSizePolicy(tabwgt._sizePolicy) # tabwgt.currentChanged.connect(tabwgt.setCurrentIndex) tabwgt.currentChanged.connect(tabwgt._on_tabletab_change) tabwgt.current_tblname = None @slot_(int) def _on_tabletab_change(tabwgt, index): """ Switch to the current imageset tab """ if VERBOSE_GUI: print('[apitab] _onchange(index=%r)' % (index,)) tblname = tabwgt.ibswgt.tblname_list[index] tabwgt.current_tblname = tblname if VERBOSE_GUI: print('[apitab] _onchange(tblname=%r)' % (tblname,)) tabwgt.ibswgt.back._clear_selection() view = tabwgt.ibswgt.views[tblname] selected = view.selectionModel().selection() deselected = QtCore.QItemSelection() tabwgt.ibswgt.update_selection(selected, deselected)
[docs]class ImageSetTabWidget(QtWidgets.QTabWidget): """ Handles tabs that contain individual image sets. """ def __init__(imageset_tabwgt, parent=None, horizontalStretch=1): QtWidgets.QTabWidget.__init__(imageset_tabwgt, parent) imageset_tabwgt.ibswgt = parent imageset_tabwgt.setTabsClosable(True) imageset_tabwgt.setMaximumSize(9999, gt.get_cplat_tab_height()) imageset_tabwgt.tabbar = imageset_tabwgt.tabBar() imageset_tabwgt.tabbar.setMovable(False) imageset_tabwgt.setStyleSheet('border: none;') imageset_tabwgt.tabbar.setStyleSheet('border: none;') sizePolicy = gt.newSizePolicy( imageset_tabwgt, horizontalStretch=horizontalStretch ) imageset_tabwgt.setSizePolicy(sizePolicy) imageset_tabwgt.tabCloseRequested.connect(imageset_tabwgt._close_tab) imageset_tabwgt.currentChanged.connect(imageset_tabwgt._on_imagesettab_change) imageset_tabwgt.imgsetid_list = [] # TURNING ON / OFF ALL IMAGES # imageset_tabwgt._add_imageset_tab(-1, const.ALL_IMAGE_IMAGESETTEXT) @slot_(int) def _on_imagesettab_change(imageset_tabwgt, index): """ Switch to the current imageset tab """ if VERBOSE_GUI: print('[imageset_tab_widget] _onchange(index=%r)' % (index,)) if 0 <= index and index < len(imageset_tabwgt.imgsetid_list): imgsetid = imageset_tabwgt.imgsetid_list[index] if VERBOSE_GUI: print('[IMAGESETTAB.ONCHANGE] imgsetid = %r' % (imgsetid,)) imageset_tabwgt.ibswgt._change_imageset(imgsetid) else: imageset_tabwgt.ibswgt._change_imageset(-1) @slot_(int) def _close_tab(imageset_tabwgt, index): print('[imageset_tab_widget] _close_tab(index=%r)' % (index,)) if imageset_tabwgt.imgsetid_list[index] is not None: imageset_tabwgt.imgsetid_list.pop(index) imageset_tabwgt.removeTab(index) @slot_() def _close_all_tabs(imageset_tabwgt): if VERBOSE_GUI: print('[imageset_tab_widget] _close_all_tabs()') while len(imageset_tabwgt.imgsetid_list) > 0: index = 0 imageset_tabwgt.imgsetid_list.pop(index) imageset_tabwgt.removeTab(index) @slot_(int) def _close_tab_with_imgsetid(imageset_tabwgt, imgsetid): print('[imageset_tab_widget] _close_tab_with_imgsetid(imgsetid=%r)' % (imgsetid)) try: index = imageset_tabwgt.imgsetid_list.index(imgsetid) imageset_tabwgt._close_tab(index) except Exception: pass def _add_imageset_tab(imageset_tabwgt, imgsetid, imagesettext): if VERBOSE_GUI: print( '[_add_imageset_tab] imgsetid=%r, imagesettext=%r' % (imgsetid, imagesettext) ) if imgsetid not in imageset_tabwgt.imgsetid_list: if VERBOSE_GUI: print('[_add_imageset_tab] adding new image tab') imageset_tabwgt.imgsetid_list.append(imgsetid) index = len(imageset_tabwgt.imgsetid_list) - 1 tab_name = str(imagesettext) # Only has a tab, doesn't actually contain anything hack_newtab = QtWidgets.QWidget(parent=imageset_tabwgt) imageset_tabwgt.addTab(hack_newtab, tab_name) else: if VERBOSE_GUI: print('[_add_imageset_tab] using existing image tab') index = imageset_tabwgt.imgsetid_list.index(imgsetid) if VERBOSE_GUI: print('[_add_imageset_tab] setCurrentIndex(index=%r)' % (index,)) imageset_tabwgt.setCurrentIndex(index) # Dont call this, it is triggered twice # imageset_tabwgt._on_imagesettab_change(index) def _update_imageset_tab_name(imageset_tabwgt, imgsetid, imagesettext): for index, _id in enumerate(imageset_tabwgt.imgsetid_list): if imgsetid == _id: imageset_tabwgt.setTabText(index, imagesettext)
############################# ######## Main Widget ######## #############################
[docs]class IBEISMainWindow(QtWidgets.QMainWindow): quitSignal = QtCore.pyqtSignal() dropSignal = QtCore.pyqtSignal(list) def __init__(mainwin, back=None, ibs=None, parent=None): super(IBEISMainWindow, mainwin).__init__(parent) # Menus try: mainwin.setUnifiedTitleAndToolBarOnMac(False) except AttributeError as ex: ut.printex( ex, 'setUnifiedTitleAndToolBarOnMac is not working', iswarning=True ) guimenus.setup_menus(mainwin, back) # Central Widget mainwin.ibswgt = IBEISGuiWidget(back=back, ibs=ibs, parent=mainwin) mainwin.setCentralWidget(mainwin.ibswgt) mainwin.setAcceptDrops(True) if back is not None: mainwin.quitSignal.connect(back.quit) else: raise AssertionError('need backend') mainwin.dropSignal.connect(mainwin.ibswgt.imagesDropped) # mainwin.resize(900, 600)
[docs] @slot_() def closeEvent(mainwin, event): event.accept() mainwin.quitSignal.emit()
[docs] def dragEnterEvent(self, event): if event.mimeData().hasUrls: event.accept() else: event.ignore()
[docs] def dropEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() links = [] for url in event.mimeData().urls(): links.append(str(url.toLocalFile())) self.dropSignal.emit(links) else: event.ignore()
[docs] @slot_() def expand_names_tree(mainwin): view = mainwin.ibswgt.views[gh.NAMES_TREE] view.expandAll()
############################# ##### IBEIS GUI Widget ###### ############################# IBEIS_WIDGET_BASE = QtWidgets.QWidget
[docs]class IBEISGuiWidget(IBEIS_WIDGET_BASE): """ CommandLine: # Testing python -m wbia --db NNP_Master3 --onlyimgtbl python -m wbia --db PZ_Master1 --onlyimgtbl """ def __init__(ibswgt, back=None, ibs=None, parent=None): super(IBEISGuiWidget, ibswgt).__init__(parent) ibswgt.ibs = ibs ibswgt.back = back # Structures that will hold models and views ibswgt.models = {} ibswgt.views = {} # FIXME: Duplicate models # Create models and views # Define the abstract item models and views for the tables ibswgt.tblname_list = [] ibswgt.modelview_defs = [] # NEW DYNAMIC WAY OF USING TABS AND API VIEWS # ADD IMAGE TABLE if True: ibswgt.tblname_list.append(IMAGE_TABLE) ibswgt.modelview_defs.append( (IMAGE_TABLE, IBEISTableWidget, IBEISItemModel, IBEISTableView) ) # ADD IMAGE GRID USE_GRID = False if USE_GRID and not ut.get_argflag('--onlyimgtbl'): ibswgt.tblname_list.append(IMAGE_GRID) ibswgt.modelview_defs.append( (IMAGE_GRID, IBEISTableWidget, IBEISStripeModel, IBEISTableView) ) # ADD ANNOT GRID if not (ut.get_argflag('--noannottbl') or ut.get_argflag('--onlyimgtbl')): ibswgt.tblname_list.append(gh.ANNOTATION_TABLE) ibswgt.modelview_defs.append( (gh.ANNOTATION_TABLE, IBEISTableWidget, IBEISItemModel, IBEISTableView) ) # ADD NAME TREE if not (ut.get_argflag('--nonametree') or ut.get_argflag('--onlyimgtbl')): ibswgt.tblname_list.append(NAMES_TREE) ibswgt.modelview_defs.append( (NAMES_TREE, IBEISTreeWidget, IBEISItemModel, IBEISTreeView) ) # ADD IMAGESET TABLE ibswgt.super_tblname_list = ibswgt.tblname_list + [IMAGESET_TABLE] ibswgt.modelview_defs.append( (IMAGESET_TABLE, ImagesetTableWidget, ImagesetTableModel, ImagesetTableView) ) # DO INITALIZATION # Create and layout components ibswgt._init_components() ibswgt._connect_signals_and_slots() ibswgt.connect_wbia_control(ibswgt.ibs) # Lazy load data every so often ibswgt.data_load_timer = QtCore.QTimer() ibswgt.data_load_timer.timeout.connect(ibswgt.data_load_loop) ibswgt.data_load_freq = 1000 # ibswgt.tt = None ibswgt.data_load_timer.start(ibswgt.data_load_freq) # @QtCore.pyqtSlot
[docs] def data_load_loop(ibswgt): # Get the current view view = ibswgt._tables_tab_widget.currentWidget() model = view.model() if model.canFetchMore(None): model.fetchMore(None)
# if ibswgt.tt is None: # ellapsed = ibswgt.data_load_freq # else: # ellapsed = ut.toc(ibswgt.tt) # print('Load more data for %r' % (view,)) ##freq = ibswgt.data_load_freq / (1000 * 2) ##frac = freq / ellapsed ##print('ibswgt.data_load_freq = %r' % (freq,)) ##print('ellapsed = %r' % (ellapsed,)) ##print('frac = %r' % (frac,)) ##if model.batch_size is not None: # #new_batch_size = model.batch_size # #new_batch_size = int(new_batch_size * frac * 2) # #new_batch_size = max(new_batch_size * 2, 2) # #new_batch_size = min(model.batch_size * 2, new_batch_size) # #new_batch_size = min(100, new_batch_size) # #model.batch_size = new_batch_size # #print('model.batch_size = %r' % (model.batch_size,)) ##ibswgt.tt = ut.tic() # else: # ibswgt.tt = None def _init_components(ibswgt): """ Defines gui components and inits layout """ # Layout ibswgt.vlayout = QtWidgets.QVBoxLayout(ibswgt) ibswgt.setLayout(ibswgt.vlayout) ibswgt.vsplitter = gt.newSplitter(ibswgt, orientation=Qt.Vertical) ibswgt.hsplitter = gt.newSplitter( ibswgt, orientation=Qt.Horizontal, verticalStretch=18 ) # # Tables Tab ibswgt._tables_tab_widget = APITabWidget(parent=ibswgt, horizontalStretch=81) for tblname, WidgetClass, ModelClass, ViewClass in ibswgt.modelview_defs: # Make view first to pass as parent ibswgt.views[tblname] = ViewClass(parent=ibswgt) # FIXME: It is very bad to give the model a view. Only the view # should have a model ibswgt.models[tblname] = ModelClass(parent=ibswgt.views[tblname]) # Connect models and views for tblname in ibswgt.super_tblname_list: ibswgt.views[tblname].setModel(ibswgt.models[tblname]) # Add Image, ANNOTATION, and Names as tabs for tblname in ibswgt.tblname_list: ibswgt._tables_tab_widget.addTab(ibswgt.views[tblname], tblname) # Force full loading ibswgt.models[IMAGE_TABLE].batch_size = 1000 ibswgt.models[IMAGESET_TABLE].batch_size = 1000 ibswgt.models[ANNOTATION_TABLE].batch_size = 1000 ibswgt.models[NAMES_TREE].batch_size = 2 ibswgt.models[NAMES_TREE].batch_size = 100 # Custom ImageSet Tab Wiget ibswgt.imageset_tabwgt = ImageSetTabWidget(parent=ibswgt) ibswgt.vlayout.addWidget(ibswgt.vsplitter) ibswgt.vsplitter.addWidget(ibswgt.hsplitter) ibswgt.status_wgt = status_wgt = gt.newWidget( ibswgt.vsplitter, orientation=Qt.Vertical, spacing=3, margin=0, name='StatusWidget', ) ibswgt.vsplitter.addWidget(status_wgt) # On the LEFT add the the table of ImageSets imgset_table_view = ibswgt.views[IMAGESET_TABLE] imgset_table_view.setSizePolicy( gt.newSizePolicy(hSizePolicy='Expanding', hStretch=2) ) # On the RIGHT add the DataTables and tabs right_hack_wgt = gt.newWidget() right_hack_wgt.addWidget(ibswgt.imageset_tabwgt) right_hack_wgt.addWidget(ibswgt._tables_tab_widget) right_hack_wgt.setSizePolicy(gt.newSizePolicy(horizontalStretch=5)) # Hack because the tables aren't actually belonging to the tabs # They are just controlled by the change ibswgt.hsplitter.addWidget(imgset_table_view) ibswgt.hsplitter.addWidget(right_hack_wgt) _NEWLBL = functools.partial(gt.newLabel, ibswgt) _NEWBUT = functools.partial(gt.newButton, ibswgt) # _COMBO = functools.partial(gt.newComboBox, ibswgt) _NEWTEXT = functools.partial(gt.newLineEdit, ibswgt, verticalStretch=1) primary_fontkw = dict(bold=True, pointSize=11) secondary_fontkw = dict(bold=False, pointSize=9) # advanced_fontkw = dict(bold=False, pointSize=8, italic=True) identify_color = (255, 150, 0) ibswgt.tablename_to_status_widget_index = { IMAGESET_TABLE: 1, IMAGE_TABLE: 3, IMAGE_GRID: 3, gh.ANNOTATION_TABLE: 5, NAMES_TREE: 7, NAME_TABLE: 7, } ibswgt.key_to_objnice = { IMAGESET_TABLE: 'ImageSet', IMAGE_TABLE: 'Image', ANNOTATION_TABLE: 'Annotation', NAMES_TREE: 'Name', } ibswgt.status_widget_list = [ _NEWLBL('Selected ImageSet: ', fontkw=secondary_fontkw, align='right'), _NEWTEXT(enabled=True, readOnly=True), ## _NEWLBL('Selected Image: ', fontkw=secondary_fontkw, align='right'), _NEWTEXT( enabled=True, readOnly=False, editingFinishedSlot=ibswgt.select_image_text_editing_finished, ), ## _NEWLBL('Selected Annotation: ', fontkw=secondary_fontkw, align='right'), _NEWTEXT( enabled=True, readOnly=False, editingFinishedSlot=ibswgt.select_annot_text_editing_finished, ), ## _NEWLBL('Selected Name: ', fontkw=secondary_fontkw, align='right'), _NEWTEXT( enabled=True, readOnly=False, editingFinishedSlot=ibswgt.select_name_text_editing_finished, ), ] back = ibswgt.back ibswgt.batch_intra_occurrence_query_button = _NEWBUT( '4) ID Encounters', functools.partial( back.compute_queries, daids_mode=const.INTRA_OCCUR_KEY, query_is_known=None, use_prioritized_name_subset=False, cfgdict={'can_match_samename': False, 'use_k_padding': False}, ), bgcolor=color_funcs.adjust_hsv_of_rgb255(identify_color, -0.01, -0.7, 0.0), fgcolor=(0, 0, 0), fontkw=primary_fontkw, ) ibswgt.batch_vsexemplar_query_button = _NEWBUT( '5) ID Exemplars', functools.partial( back.compute_queries, daids_mode=const.VS_EXEMPLARS_KEY, use_prioritized_name_subset=True, query_is_known=None, cfgdict={'can_match_samename': False, 'use_k_padding': False}, ), bgcolor=color_funcs.adjust_hsv_of_rgb255(identify_color, -0.02, -0.7, 0.0), fgcolor=(0, 0, 0), fontkw=primary_fontkw, ) ibswgt.import_button = _NEWBUT( '1) Import', # back.import_images_from_dir, back.import_button_click, bgcolor=(235, 200, 200), fontkw=primary_fontkw, ) ibswgt.imageset_button = _NEWBUT( '2) Group', ibswgt.back.do_group_occurrence_step, bgcolor=(255, 255, 150), fontkw=primary_fontkw, ) ibswgt.detect_button = _NEWBUT( '3) Detect', ibswgt.back.run_detection_step, bgcolor=(150, 255, 150), fontkw=primary_fontkw, ) ibswgt.reviewed_button = _NEWBUT( '6) Complete', ibswgt.back.commit_to_wb_step, bgcolor=color_funcs.adjust_hsv_of_rgb255((0, 232, 211), 0.0, -0.9, 0.0), fontkw=primary_fontkw, enabled=True, ) ibswgt.control_widget_lists = [ [ ibswgt.import_button, ibswgt.imageset_button, _NEWLBL(''), ibswgt.detect_button, ibswgt.batch_intra_occurrence_query_button, ibswgt.batch_vsexemplar_query_button, ibswgt.reviewed_button, ], [ _NEWBUT( 'Advanced ID Interface', back.show_advanced_id_interface, bgcolor=color_funcs.adjust_hsv_of_rgb255(identify_color), fgcolor=(0, 0, 0), fontkw=primary_fontkw, ) ], ] # Other components # New widget has black magic (for implicit layouts) in it # Add control widgets (import, group, species selector, etc...) for count, control_widgets in enumerate(ibswgt.control_widget_lists): _container = status_wgt.addNewWidget( orientation=Qt.Horizontal, margin=0, name='ControlContainer%d' % (count,), ) for widget in control_widgets: _container.addWidget(widget) # Output log (turned off by default) ibswgt.outputLog = gt.newOutputLog( status_wgt, pointSize=8, visible=WITH_GUILOG, verticalStretch=6 ) status_wgt.addWidget(ibswgt.outputLog) # Add selected ids status widget ibswgt.selectionStatusWidget = status_wgt.addNewWidget( orientation=Qt.Horizontal, margin=3, name='SelectionStatus' ) for widget in ibswgt.status_widget_list: ibswgt.selectionStatusWidget.addWidget(widget) # Progress bar is at the bottom ibswgt.prog_bar = status_wgt.addNewProgressBar( visible=False, verticalStretch=1, name='prog_bar' ) # ibswgt.vsplitter.print_widget_heirarchy(max_depth=4) def _connect_signals_and_slots(ibswgt): if VERBOSE_GUI: print('[newgui] _connect_signals_and_slots') for tblname in ibswgt.super_tblname_list: tblview = ibswgt.views[tblname] tblview.doubleClicked.connect(ibswgt.on_doubleclick) tblview.contextMenuClicked.connect(ibswgt.on_contextMenuClicked) if tblname != gh.IMAGESET_TABLE: tblview.selectionModel().selectionChanged.connect(ibswgt.update_selection) # front.printSignal.connect(back.backend_print) # front.raiseExceptionSignal.connect(back.backend_exception) # CONNECT HOOK TO GET NUM ROWS tblview.rows_updated.connect(ibswgt.on_rows_updated)
[docs] @slot_(QtCore.QItemSelection, QtCore.QItemSelection) def update_selection(ibswgt, selected, deselected): """ Quirky behavior: if you select two columns in a row and then unselect only one, the whole row is unselected, because this function only deals with deltas. Example: >>> # DISABLE_DOCTEST >>> from wbia.gui.newgui import * # NOQA >>> ibs, back, ibswgt, testdata_main_loop = testdata_guifront() >>> ibswgt.set_table_tab(gh.NAMES_TREE) >>> view = ibswgt.views[gh.NAMES_TREE] >>> view.expandAll() >>> AUTOSELECT = False >>> if AUTOSELECT: ... view.selectAll() >>> selmodel = view.selectionModel() >>> selected = selmodel.selection() >>> deselected = QtCore.QItemSelection() >>> # verify results >>> print(result) """ # print('[ibswgt] update selection') # print('selected = ' + str(selected.indexes())) # print('deselected = ' + str(deselected.indexes())) deselected_model_index_list_ = deselected.indexes() selected_model_index_list_ = selected.indexes() def get_selection_info(model_index_list_): model_index_list = [ qtindex for qtindex in model_index_list_ if qtindex.isValid() ] model_list = [qtindex.model() for qtindex in model_index_list] tablename_list = [model.name for model in model_list] level_list = [ model._get_level(qtindex) for model, qtindex in zip(model_list, model_index_list) ] rowid_list = [ model._get_row_id(qtindex) for model, qtindex in zip(model_list, model_index_list) ] table_key_list = list(zip(tablename_list, level_list)) return table_key_list, rowid_list select_table_key_list, select_rowid_list = get_selection_info( selected_model_index_list_ ) deselect_table_key_list, deselect_rowid_list = get_selection_info( deselected_model_index_list_ ) table_key2_selected_rowids = dict( ut.group_items(select_rowid_list, select_table_key_list) ) table_key2_deselected_rowids = dict( ut.group_items(deselect_rowid_list, deselect_table_key_list) ) table_key2_selected_rowids = { key: list(set(val)) for key, val in six.iteritems(table_key2_selected_rowids) } table_key2_deselected_rowids = { key: list(set(val)) for key, val in six.iteritems(table_key2_deselected_rowids) } if ut.VERBOSE: print('table_key2_selected_rowids = ' + ut.repr2(table_key2_selected_rowids)) print( 'table_key2_deselected_rowids = ' + ut.repr2(table_key2_deselected_rowids) ) gh_const_tablename_map = { (IMAGE_TABLE, 0): const.IMAGE_TABLE, (IMAGE_GRID, 0): const.IMAGE_TABLE, (gh.ANNOTATION_TABLE, 0): const.ANNOTATION_TABLE, (NAME_TABLE, 0): const.NAME_TABLE, (NAMES_TREE, 0): const.NAME_TABLE, (NAMES_TREE, 1): const.ANNOTATION_TABLE, } # here tablename is a backend const tablename for table_key, id_list in six.iteritems(table_key2_deselected_rowids): tablename = gh_const_tablename_map[table_key] ibswgt.back._set_selection3(tablename, id_list, mode='diff') for table_key, id_list in six.iteritems(table_key2_selected_rowids): tablename = gh_const_tablename_map[table_key] ibswgt.back._set_selection3(tablename, id_list, mode='add') ibswgt.back.update_selection_texts()
# tblview.selectionModel().selectedIndexes()
[docs] def changing_models_gen(ibswgt, tblnames=None): """ Loops over tablenames emitting layoutChanged at the end for each """ tblnames = ibswgt.super_tblname_list if tblnames is None else tblnames if VERBOSE_GUI: print('[newgui] changing_models_gen(tblnames=%r)' % (tblnames,)) model_list = [ibswgt.models[tblname] for tblname in tblnames] # model_list = [ibswgt.models[tblname] for tblname in tblnames if # ibswgt.views[tblname].isVisible()] with ChangeLayoutContext(model_list): for tblname in tblnames: yield tblname
[docs] def update_tables(ibswgt, tblnames=None, clear_view_selection=True): """ forces changing models """ print('[newgui] update_tables(%r)' % (tblnames,)) hack_selections = [] # print('[new_gui.UPDATE_TABLES]') for tblname in ibswgt.changing_models_gen(tblnames=tblnames): # print('[new_gui.update_tables] tblname=%r' % (tblname, )) model = ibswgt.models[tblname] view = ibswgt.views[tblname] if clear_view_selection: hack_selections.append(view.clearSelection) model._update() # Hack: Call this outside changing models gen for clearSelection in hack_selections: clearSelection()
[docs] def connect_wbia_control(ibswgt, ibs): """ Connects a new ibscontroler to the models """ if VERBOSE_GUI: print('[newgui] connect_wbia_control(ibs=%r)' % (ibs,)) ibswgt.imageset_tabwgt._close_all_tabs() if ibs is None: if VERBOSE_GUI: print('[newgui] invalid ibs') title = 'No Database Opened' ibswgt.setWindowTitle(title) else: if VERBOSE_GUI: print('[newgui] Connecting valid ibs=%r' % ibs.get_dbname()) # with ut.Indenter('[CONNECTING]'): # Give the frontend the new control ibswgt.ibs = ibs if not ut.get_argflag('--fast'): with ut.Timer('update special', verbose=VERBOSE_GUI): if not ibs.readonly: ibs.update_special_imagesets() else: if VERBOSE_GUI: print('Skipping special imagesets') # Update the api models to use the new control with ut.Timer('make headers', verbose=VERBOSE_GUI): header_dict, declare_tup = gh.make_wbia_headers_dict(ibswgt.ibs) ibswgt.declare_tup = declare_tup def get_title(ibs): """ DEPRICATE OR MOVE """ if ibs is None: title = 'IBEIS - No Database Directory Open' elif ibs.dbdir is None: title = 'IBEIS - !! INVALID DATABASE !!' else: dbdir = ibs.get_dbdir() dbname = ibs.get_dbname() title = 'IBEIS - %r - Database Directory = %s' % (dbname, dbdir) wb_target = ibs.const.WILDBOOK_TARGET # params.args.wildbook_target if wb_target is not None: title = '%s - Wildbook Target = %s' % (title, wb_target) return title title = get_title(ibswgt.ibs) ibswgt.setWindowTitle(title) if ut.VERBOSE: print('[newgui] Calling model _update_headers') # block_wgt_flag = ibswgt._tables_tab_widget.blockSignals(True) with ut.Timer('[newgui] update models', verbose=VERBOSE_GUI): # for tblname in ibswgt.changing_models_gen(ibswgt.super_tblname_list): for tblname in ibswgt.super_tblname_list: model = ibswgt.models[tblname] view = ibswgt.views[tblname] # if not view.isVisible(): # print(view) # ut.embed() header = header_dict[tblname] # widget = ibswgt.widgets[tblname] # widget.change_headers(header) # NOT SURE IF THESE BLOCKERS SHOULD BE COMMENTED # block_model_flag = model.blockSignals(True) model._update_headers(**header) view._update_headers(**header) # should use model headers # model.blockSignals(block_model_flag) # # view.infer_delegates_from_model() for tblname in ibswgt.super_tblname_list: view = ibswgt.views[tblname] # if not view.isVisible(): # print(view) # continue view.hide_cols() # ibswgt._tables_tab_widget.blockSignals(block_wgt_flag) # Update species with ones enabled in database if not ibs.readonly: ibswgt.update_species_available() # FIXME: bad code # TODO: load previously loaded imageset or nothing LOAD_IMAGESET_ON_START = not ut.get_argflag('--fast') if LOAD_IMAGESET_ON_START: imgsetid_list = ibs.get_valid_imgsetids(shipped=False) if len(imgsetid_list) > 0: DEFAULT_LARGEST_IMAGESET = False if DEFAULT_LARGEST_IMAGESET: numImg_list = ibs.get_imageset_num_gids(imgsetid_list) argx = ut.list_argsort(numImg_list, reverse=True)[0] imgsetid = imgsetid_list[argx] else: # Grab "first" imageset imgsetid = imgsetid_list[0] # ibswgt._change_imageset(imgsetid) ibswgt.select_imageset_tab(imgsetid) else: ibswgt._change_imageset(-1)
[docs] def update_species_available( ibswgt, reselect=False, reselect_new_name=None, deleting=False ): ibs = ibswgt.ibs # TODO: update these options depending on ibs.get_species_with_detectors # when a controller is attached to the gui detection_combo_box_options = [ # Text # Value # ('Select Species', 'none'), ('Select Species', const.UNKNOWN), ('Unknown', const.UNKNOWN), #'none'), ] + sorted(list(ibs.get_working_species())) species_text = ibswgt.back.get_selected_species() reselect_index = None if not deleting and reselect_new_name is None and species_text is not None: species_rowid = ibs.get_species_rowids_from_text(species_text) reselect_new_name = ibs.get_species_nice(species_rowid) if VERBOSE_GUI: print('[front] Reselecting old selection: %r' % (reselect_new_name,)) nice_name_list = [str(_[0]) for _ in detection_combo_box_options] if reselect_new_name in nice_name_list: reselect_index = nice_name_list.index(reselect_new_name) if VERBOSE_GUI: print('[front] Reselecting renamed selection: %r' % (reselect_new_name,)) if VERBOSE_GUI: print('[front] Reselecting index: %r' % (reselect_index,))
# ibswgt.species_combo.setOptions(detection_combo_box_options) # ibswgt.species_combo.updateOptions(reselect=reselect, reselect_index=reselect_index)
[docs] def setWindowTitle(ibswgt, title): parent_ = ibswgt.parent() if parent_ is not None: parent_.setWindowTitle(title) else: IBEIS_WIDGET_BASE.setWindowTitle(ibswgt, title)
def _change_imageset(ibswgt, imgsetid): if VERBOSE_GUI: print( '[newgui] _change_imageset(imgsetid=%r, uuid=%r)' % (imgsetid, ibswgt.back.ibs.get_imageset_uuid(imgsetid)) ) for tblname in ibswgt.tblname_list: view = ibswgt.views[tblname] view.clearSelection() for tblname in ibswgt.changing_models_gen(tblnames=ibswgt.tblname_list): view = ibswgt.views[tblname] view._change_imageset(imgsetid) try: ibswgt.back.select_imgsetid(imgsetid) except Exception as ex: ut.printex(ex, iswarning=True) ibswgt.set_table_tab(IMAGE_TABLE) def _update_imageset_tab_name(ibswgt, imgsetid, imagesettext): ibswgt.imageset_tabwgt._update_imageset_tab_name(imgsetid, imagesettext) # ------------ # SLOT HELPERS # ------------
[docs] def get_table_tab_index(ibswgt, tblname): view = ibswgt.views[tblname] index = ibswgt._tables_tab_widget.indexOf(view) return index
[docs] def set_selection_status(ibswgt, key, ids): ids = list(ids) text = repr(ids) index = ibswgt.tablename_to_status_widget_index[key] if len(ids) <= 1: text2 = 'Selected %s:' % (ibswgt.key_to_objnice[key],) else: text2 = 'Selected %s %s:' % ( len(ids), ut.pluralize(ibswgt.key_to_objnice[key], len(ids)), ) ibswgt.status_widget_list[index - 1].setText(text2) ibswgt.status_widget_list[index].setText(text)
[docs] def set_table_tab(ibswgt, tblname): """ Programmatically change to the table-tab to either: Image, ImageGrid, Annotation, or Names table/tree Example: >>> # xdoctest: +REQUIRES(--gui) >>> from wbia.gui.newgui import * # NOQA >>> ibs, back, ibswgt, testdata_main_loop = testdata_guifront() >>> ibswgt.set_table_tab(gh.ANNOTATION_TABLE) """ if VERBOSE_GUI: print('[newgui] set_table_tab: %r ' % (tblname,)) with ut.Timer('set table tab', verbose=VERBOSE_GUI): index = ibswgt.get_table_tab_index(tblname) ibswgt._tables_tab_widget.setCurrentIndex(index)
[docs] def select_imageset_tab(ibswgt, imgsetid): if VERBOSE_GUI: print('[newgui] select_imageset_tab imgsetid=%r' % (imgsetid,)) if isinstance(imgsetid, six.string_types): # Hack imagesettext = imgsetid imgsetid = ibswgt.ibs.get_imageset_imgsetids_from_text(imagesettext) else: imagesettext = ibswgt.ibs.get_imageset_text(imgsetid) # ibswgt.back.select_imgsetid(imgsetid) ibswgt.imageset_tabwgt._add_imageset_tab(imgsetid, imagesettext)
[docs] def spawn_edit_image_annotation_interaction_from_aid( ibswgt, aid, imgsetid, model=None, qtindex=None ): """ hack for letting annots spawn image editing CommandLine: python -m wbia.gui.newgui spawn_edit_image_annotation_interaction_from_aid --show Example: >>> # DISABLE_DOCTEST >>> from wbia.gui.newgui import * # NOQA >>> import wbia >>> main_locals = wbia.main(defaultdb='testdb1') >>> ibs, back = ut.dict_take(main_locals, ['ibs', 'back']) >>> ibswgt = back.ibswgt # NOQA >>> aid = 4 >>> imgsetid = 1 >>> ibswgt.spawn_edit_image_annotation_interaction_from_aid(aid, imgsetid) >>> if ut.show_was_requested(): >>> gt.qtapp_loop(qwin=ibswgt) """ gid = ibswgt.back.ibs.get_annot_gids(aid) if model is None: view = ibswgt.views[IMAGE_TABLE] model = view.model() qtindex, row = view.get_row_and_qtindex_from_id(gid) ibswgt.spawn_edit_image_annotation_interaction(model, qtindex, gid, imgsetid)
[docs] def spawn_edit_image_annotation_interaction(ibswgt, model, qtindex, gid, imgsetid): """ TODO: needs reimplement using more standard interaction methods """ print('[newgui] Creating new annotation interaction: gid=%r' % (gid,)) ibs = ibswgt.ibs # Select gid ibswgt.back.select_gid(gid, imgsetid, show=False) # Interact with gid nextcb, prevcb, current_gid = ibswgt._interactannot2_callbacks(model, qtindex) iannot2_kw = { 'rows_updated_callback': ibswgt.update_tables, 'next_callback': nextcb, 'prev_callback': prevcb, } assert current_gid == gid, 'problem in next/prev updater' ibswgt.annot_interact = interact_annotations2.ANNOTATION_Interaction2( ibs, gid, **iannot2_kw ) # hacky GID_PROG: TODO: FIX WITH OTHER HACKS OF THIS TYPE # FIXME; this should depend on the model. # _, row = model.view.get_row_and_qtindex_from_id(gid) # pt.set_figtitle('%d/%d' % (row + 1, model.rowCount())) level_num_rows = model._get_level_row_count(qtindex) level_row = model._get_level_row_index(qtindex) pt.set_figtitle('%d/%d' % (level_row + 1, level_num_rows))
[docs] def make_adjacent_qtindex_callbacks(ibswgt, model, qtindex): r""" Returns: tuple: (current_rowid, next_callback, prev_callback) CommandLine: python -m wbia.gui.newgui --test-make_adjacent_qtindex_callbacks Example: >>> # DISABLE_DOCTEST >>> from wbia.gui.newgui import * # NOQA >>> ibs, back, ibswgt, testdata_main_loop = testdata_guifront() >>> gid = ibs.get_valid_gids()[0] >>> model = ibswgt.models[gh.IMAGE_TABLE] >>> qtindex, row = model.get_row_and_qtindex_from_id(gid) >>> tup = ibswgt.make_adjacent_qtindex_callbacks(model, qtindex) >>> (current_rowid, next_callback, prev_callback) = tup >>> assert prev_callback is None, 'should not be a previous image id' >>> current_rowid1, next_callback1, prev_callback1 = next_callback() >>> assert next_callback() is None, 'race condition not prevented' >>> current_rowid2, next_callback2, prev_callback2 = next_callback1() >>> testdata_main_loop(globals(), locals()) """ current_rowid = model._get_row_id(qtindex) next_qtindex = model._get_adjacent_qtindex(qtindex, 1) prev_qtindex = model._get_adjacent_qtindex(qtindex, -1) next_callback = None prev_callback = None numclicks = [0] # semephore, invalidates both functions after one call if next_qtindex is not None and next_qtindex.isValid(): def next_callback(): if numclicks[0] != 0: print('race condition in next_callback %d ' % numclicks[0]) return numclicks[0] += 1 return ibswgt.make_adjacent_qtindex_callbacks(model, next_qtindex) if prev_qtindex is not None and prev_qtindex.isValid(): def prev_callback(): if numclicks[0] != 0: print('race condition in next_callback %d ' % numclicks[0]) return numclicks[0] += 1 return ibswgt.make_adjacent_qtindex_callbacks(model, next_qtindex) return current_rowid, next_callback, prev_callback
def _interactannot2_callbacks(ibswgt, model, qtindex): """ callbacks for the edit image annotation (from image table) interaction python -m wbia --db lynx --imgsetid 2 TODO: needs reimplement """ # if not qtindex.isValid(): # raise AssertionError('Bug: qtindex got invalidated') # # BUG: somewhere qtindex gets invalidated # #return None, None, -1 # HACK FOR NEXT AND PREVIOUS CLICK CALLBACKS # print('model.name = %r' % (model.name,)) if model.name == gh.IMAGE_TABLE: cur_gid = model._get_row_id(qtindex) # elif model.name == gh.IMAGE_GRID: # cur_gid = model._get_row_id(qtindex) elif model.name == gh.NAMES_TREE: cur_level = model._get_level(qtindex) if cur_level == 1: cur_aid = model._get_row_id(qtindex) cur_gid = ibswgt.ibs.get_annot_gids(cur_aid) else: raise NotImplementedError( 'Unknown model.name=%r, cur_level=%r' % (model.name, cur_level) ) else: print('gh.IMAGE_TABLE = %r' % (gh.IMAGE_TABLE,)) raise NotImplementedError('Unknown model.name =%r' % (model.name,)) next_qtindex = model._get_adjacent_qtindex(qtindex, 1) prev_qtindex = model._get_adjacent_qtindex(qtindex, -1) numclicks = [0] # semephore def make_qtindex_callback(qtindex_, type_='nextprev'): def _qtindex_callback(): if numclicks[0] != 0: print('race condition in %s_callback %d ' % (type_, numclicks[0])) return numclicks[0] += 1 # call this function again with next index nextcb, prevcb, new_gid1 = ibswgt._interactannot2_callbacks( model, qtindex_ ) print('[newgui] %s_callback: new_gid1=%r' % (type_, new_gid1)) ibswgt.annot_interact.update_image_and_callbacks( new_gid1, nextcb, prevcb, do_save=True ) # hacky GID_PROG: TODO: FIX WITH OTHER HACKS OF THIS TYPE # _, row = model.view.get_row_and_qtindex_from_id(new_gid1) # pt.set_figtitle('%d/%d' % (row + 1, model.rowCount())) level_num_rows = model._get_level_row_count(qtindex_) level_row = model._get_level_row_index(qtindex_) pt.set_figtitle('%d/%d' % (level_row + 1, level_num_rows)) return _qtindex_callback if next_qtindex is not None and next_qtindex.isValid(): next_callback = make_qtindex_callback(next_qtindex, 'next') else: next_callback = None if prev_qtindex is not None and prev_qtindex.isValid(): prev_callback = make_qtindex_callback(prev_qtindex, 'prev') else: prev_callback = None return next_callback, prev_callback, cur_gid # ------------ # SLOTS # ------------
[docs] @slot_() def select_annot_text_editing_finished(ibswgt): tablename = gh.ANNOTATION_TABLE index = ibswgt.tablename_to_status_widget_index[tablename] text = ibswgt.status_widget_list[index].text() ibswgt.select_table_indicies_from_text(tablename, text)
[docs] @slot_() def select_name_text_editing_finished(ibswgt): tablename = gh.NAMES_TREE index = ibswgt.tablename_to_status_widget_index[tablename] text = ibswgt.status_widget_list[index].text() ibswgt.select_table_indicies_from_text(tablename, text)
[docs] @slot_() def select_image_text_editing_finished(ibswgt): tablename = gh.IMAGE_TABLE index = ibswgt.tablename_to_status_widget_index[tablename] text = ibswgt.status_widget_list[index].text() ibswgt.select_table_indicies_from_text(tablename, text)
[docs] def select_table_indicies_from_text(ibswgt, tblname, text, allow_table_change=False): """ Args: tblname - tablename of the id to parse from text Ignore: text = '[1, 2, 3,]' text = '51e10019-968b-5f2e-2287-8432464d7547 ' Example: >>> # xdoctest: +REQUIRES(--gui) >>> from wbia.gui.newgui import * # NOQA >>> ibs, back, ibswgt, testdata_main_loop = testdata_guifront() >>> ibswgt.set_table_tab(gh.ANNOTATION_TABLE) >>> tblname = gh.NAMES_TREE >>> text = 'lena' >>> ibswgt.select_table_indicies_from_text(tblname, text) """ if not ut.QUIET: print('[newgui] select_table_indicies_from_text') print('[newgui] * gh.tblname = %r' % (tblname,)) print('[newgui] * text = %r' % (text,)) to_backend_tablename = { gh.ANNOTATION_TABLE: const.ANNOTATION_TABLE, gh.NAMES_TREE: const.NAME_TABLE, gh.IMAGE_TABLE: const.IMAGE_TABLE, } backend_tablename = to_backend_tablename[tblname] if not ut.QUIET: print('[newgui] * backend_tablename = %r' % (backend_tablename,)) if text == '': text = '[]' try: # MODE1 = True # if MODE1: id_list_ = text.lstrip('[').rstrip(']').split(',') id_list = [id_.strip() for id_ in id_list_] id_list = [id_ for id_ in id_list if len(id_) > 0] try: id_list = list(map(int, id_list)) except ValueError: import uuid try: # First check to see if the text is a UUID id_list = list(map(uuid.UUID, id_list)) except ValueError: if tblname != gh.NAMES_TREE: raise else: # then maybe it was a name that was selected id_list = list(map(str, id_list)) # else: # id_list_ = eval(text, globals(), locals()) # id_list = ut.ensure_iterable(id_list_) # NOQA except Exception as ex: ut.printex(ex, iswarning=True, keys=['text']) else: if not ut.QUIET: print('[newgui] * id_list = %r' % (id_list,)) # print(id_list) id_list = ibswgt.back._set_selection3(backend_tablename, id_list, mode='set') # Select the index if we are in the right table tab if len(id_list) == 1 and ( allow_table_change or ibswgt._tables_tab_widget.current_tblname == tblname ): if not ut.QUIET: print('[newgui] * attempting to select from rowid') # view = ibswgt.views[tblname] # view.select_row_from_id(id_list[0]) ibswgt.goto_table_id(tblname, id_list[0]) else: # TODO: convert the id into the ids corresponding with this tablename and move # to the first one pass ibswgt.back.update_selection_texts()
# pass
[docs] @slot_(str, int) def on_rows_updated(ibswgt, tblname, nRows): """ When the rows are updated change the tab names """ if VERBOSE_GUI: print('[newgui] on_rows_updated: tblname=%12r nRows=%r ' % (tblname, nRows)) if tblname == IMAGESET_TABLE: # Hack # print('... tblname == IMAGESET_TABLE, ...hack return') return tblname = str(tblname) TABLE_NICE = ibswgt.declare_tup[1] # hack tblnice = TABLE_NICE[tblname] index = ibswgt.get_table_tab_index(tblname) text = tblnice + ' ' + str(nRows) # CHANGE TAB NAME TO SHOW NUMBER OF ROWS ibswgt._tables_tab_widget.setTabText(index, text)
[docs] def goto_table_id(ibswgt, tablename, _id): print('[newgui] goto_table_id(tablenamd=%r, _id=%r)' % (tablename, _id)) ibswgt.set_table_tab(tablename) view = ibswgt.views[tablename] view.select_row_from_id(_id, scroll=True)
[docs] @slot_(QtCore.QModelIndex, QtCore.QPoint) def on_contextMenuClicked(ibswgt, qtindex, pos): """ Right click anywhere in the GUI Context menus on right click of a table CommandLine: python -m wbia --db WS_ALL --imgsetid 2 --select-name=A-003 """ if not qtindex.isValid(): return model = qtindex.model() tblview = ibswgt.views[model.name] context_options = [] qtindex_list = tblview.selectedIndexes() id_list = [model._get_row_id(_qtindex) for _qtindex in qtindex_list] level_list = [model._get_level(_qtindex) for _qtindex in qtindex_list] level2_ids_ = ut.group_items(id_list, level_list) level2_ids = { level: ut.unique_ordered(ids) for level, ids in six.iteritems(level2_ids_) } ibs = ibswgt.back.ibs back = ibswgt.back col = qtindex.column() setter_func = model.col_setter_list[col] if setter_func is not None: # Make a copy of the list id_list_ = id_list[:] clone_highlight = len(id_list_) > 1 if not clone_highlight: id_list_ = [_.get_id() for _ in model.level_index_list] # Getter and current value getter_func = model.col_getter_list[col] current_id = model._get_row_id(qtindex) current_value = getter_func(current_id) current_list = [current_value] * len(id_list_) # Add to context menus def _setter_refresh(id_list_, current_list, tablename): args = ( current_list[0], len(id_list_), ) if not back.are_you_sure('Confirm cloning value %s to %d rows?' % args): return setter_func(id_list_, current_list) ibswgt.update_tables(tblnames=[tablename], clear_view_selection=False) if clone_highlight: context_options += [ ( 'Clone selected value to highlighted rows', lambda: _setter_refresh(id_list_, current_list, model.name), ), ('----', lambda: None), ] else: context_options += [ ( 'Clone selected value to all rows', lambda: _setter_refresh(id_list_, current_list, model.name), ), ('----', lambda: None), ] def build_annot_context_options(ibswgt, ibs, aid_list, imgsetid, **kwargs): context_options = [] if len(aid_list) == 1: aid = aid_list[0] if kwargs.get('goto_image', True): context_options += [ ( 'Go to image', lambda: ibswgt.goto_table_id( IMAGE_TABLE, ibswgt.back.ibs.get_annot_gids(aid) ), ) ] if kwargs.get('goto_annot', True): context_options += [ ( 'Go to annot', lambda: ibswgt.goto_table_id(gh.ANNOTATION_TABLE, aid), ) ] if kwargs.get('goto_name', True): context_options += [ ( 'Go to name', lambda: ibswgt.goto_table_id( NAMES_TREE, ibswgt.back.ibs.get_annot_nids(aid) ), ), ] if kwargs.get('canedit', True): context_options += [ ( 'Edit Annotation in Image', lambda: ibswgt.spawn_edit_image_annotation_interaction_from_aid( aid, imgsetid ), ), ] context_options += [ ( "Clone annotation's species to ImageSet", lambda: ibswgt.back.copy_species_to_imageset(aid, imgsetid), ), ] context_options += [ ('----', lambda: None), ( 'View annotation in Web', # lambda: ibswgt.back.select_aid(aid, imgsetid, show=True)), lambda: ibswgt.back.show_annotation(aid, web=True), ), ( 'View image in Web', lambda: ibswgt.back.select_gid_from_aid( aid, imgsetid, show=True, web=True ), ), ( 'Set annotation species', lambda: ibswgt.back.override_all_annotation_species(aid_list), ), ('----', lambda: None), ("Remove annotation's name", lambda: ibswgt.back.unset_names([aid]),), ('Delete annotation', lambda: ibswgt.back.delete_annot(aid_list)), ('----', lambda: None), ] from wbia.viz.interact import interact_chip from wbia import viz context_options += interact_chip.build_annot_context_options( ibswgt.back.ibs, aid, refresh_func=viz.draw, with_interact_image=False, ) else: context_options += [ ( 'View annotations in Web', lambda: ibswgt.back.show_aid_list_in_web(aid_list), ), ( 'Set annotation species', lambda: ibswgt.back.override_all_annotation_species(aid_list), ), ( "Unset annotations' names", lambda: ibswgt.back.unset_names(aid_list), ), ('Delete annotations', lambda: ibswgt.back.delete_annot(aid_list)), ] return context_options def name_context_options(ibswgt, ibs, nid_list, aid_list, imgsetid, **kwargs): context_options = [] if len(aid_list) == 1: aid = aid_list[0] context_options += build_annot_context_options( ibswgt, ibs, [aid], imgsetid ) if len(aid_list) > 0: def set_annot_names_to_same_new_name(ibswgt, aid_list): ibswgt.back.ibs.set_annot_names_to_same_new_name(aid_list) ibswgt.update_tables(tblnames=[gh.NAMES_TREE]) context_options += [ ( 'Rename annots (%s) to new name' % ut.list_str_summarized(aid_list, 'aid_list'), lambda: set_annot_names_to_same_new_name(ibswgt, aid_list), ), ] if len(nid_list) > 0: def run_splits(ibs, nid_list): print('Checking for splits') aids_list = ibs.get_name_aids(nid_list) aid_list = sorted(list(set(ut.flatten(aids_list)))) back.run_annot_splits(aid_list) def export_nids(ibs, nid_list): from wbia.dbio import export_subset if not back.are_you_sure( 'Confirm export of nid_list=%r' % (nid_list,) ): return export_subset.export_names(ibs, nid_list) def create_new_imageset_from_names_(ibs, nid_list): ibs.create_new_imageset_from_names(nid_list) ibswgt.update_tables([gh.IMAGESET_TABLE], clear_view_selection=False) context_options += [ ( 'View name(s) in Web', lambda: ibswgt.back.show_nid_list_in_web(nid_list), ), ('----', lambda: None), ('Check for splits', lambda: run_splits(ibs, nid_list)), ('Export names', lambda: export_nids(ibs, nid_list)), ( 'Create ImageSet From Name(s)', lambda: create_new_imageset_from_names_(ibs, nid_list), ), ] from wbia.viz.interact import interact_name context_options += interact_name.build_name_context_options( ibswgt.back.ibs, nid_list ) elif len(nid_list) == 0: # from wbia.viz.interact import interact_name # context_options += interact_name.build_name_context_options( # ibswgt.back.ibs, nid_list) # print('nutin') pass return context_options def build_image_context_options(ibswgt, ibs, gid_list, imgsetid, **kwargs): current_imagesettext = ibswgt.back.ibs.get_imageset_text(imgsetid) context_options = [] # Conditional context menu def view_images_in_directory(gid_list): images = ibs.images(gid_list) fpaths = images.paths ut.view_file_in_directory(fpaths) def view_images_in_web(gid_list): ibswgt.back.show_images_in_web(gid_list) fmt = { 'images': ut.pluralize('image', len(gid_list)), "images's": ut.pluralize("image's", len(gid_list)), 'annotations': ut.pluralize('annotation', len(gid_list)), 'times': ut.pluralize('time', len(gid_list)), } context_options = [ ( "Edit {images's} {times}".format(**fmt), lambda: ibswgt.edit_image_time(gid_list), ), ( 'View {images} in Web'.format(**fmt), lambda: view_images_in_web(gid_list), ), ( 'View {images} in Directory'.format(**fmt), lambda: view_images_in_directory(gid_list), ), ('---', lambda: None), ( 'Add annotation from entire {images}'.format(**fmt), lambda: ibswgt.back.add_annotation_from_image(gid_list), ), ( 'Run detection on {images} (can cause duplicates)'.format(**fmt), lambda: ibswgt.back.run_detection_on_images(gid_list), ), ] if len(gid_list) == 1: gid = gid_list[0] imgsetid = model.imgsetid aid_list = ibs.get_image_aids(gid) annot_options = [ ( 'Options aid=%r' % (aid,), build_annot_context_options( ibswgt, ibs, [aid], imgsetid, goto_image=False ), ) for aid in aid_list ] if len(aid_list) == 1: annot_option_item = ( 'Annot Options (aid=%r)' % (aid_list[0],), annot_options[0][1], ) else: annot_option_item = ('Annot Options', annot_options) if kwargs.get('goto_image_in_imgtbl', False): context_options += [ ( 'Go to image in Images Table', lambda: ibswgt.goto_table_id(IMAGE_TABLE, gid), ), ] context_options += [ ( 'View image in Matplotlib', lambda: ibswgt.back.select_gid( gid, imgsetid, show=True, web=False ), ), ( 'View detection image (Hough) [dev]', lambda: ibswgt.back.show_hough_image_(gid), ), annot_option_item, ] # Special condition for imagesets if current_imagesettext != const.NEW_IMAGESET_IMAGESETTEXT: context_options += [ ('----', lambda: None), ( 'Move to new imageset', lambda: ibswgt.back.send_to_new_imageset(gid_list, mode='move'), ), ( 'Copy to new imageset', lambda: ibswgt.back.send_to_new_imageset(gid_list, mode='copy'), ), ] if current_imagesettext != const.UNGROUPED_IMAGES_IMAGESETTEXT: context_options += [ ('----', lambda: None), ( 'Remove from imageset', lambda: ibswgt.back.remove_from_imageset(gid_list), ), ] from wbia.viz import viz_graph2 context_options += [ ('----', lambda: None), ( 'Graph interaction (Annots)', lambda: viz_graph2.make_qt_graph_interface( # ibs, nids=ut.unique(ut.flatten(ibs.get_image_nids(gid_list))), ibs, gids=gid_list, graph_tab=True, ), ), ] # Continue the conditional context menu if len(gid_list) == 1: # We get gid from above context_options += [ ('----', lambda: None), ( "Delete image's annotations", lambda: ibswgt.back.delete_image_annotations([gid]), ), ( "Set image's annotations species", lambda: ibswgt.back.override_all_annotation_species(gids=[gid]), ), ('Delete image', lambda: ibswgt.back.delete_image(gid)), ] else: context_options += [ ('----', lambda: None), ( "Delete images' annotations", lambda: ibswgt.back.delete_image_annotations(gid_list), ), ( "Set images' annotations species", lambda: ibswgt.back.override_all_annotation_species( gids=gid_list ), ), ('Delete images', lambda: ibswgt.back.delete_image(gid_list)), ] return context_options # ---- IMAGESET CONTEXT ---- if model.name == IMAGESET_TABLE: # This is for the benefit of merge imagesets merge_destination_id = model._get_row_id(qtindex) imagesettext = ibswgt.back.ibs.get_imageset_text(merge_destination_id) imgsetid_list = level2_ids[0] # Conditional context menu # TODO: remove duplicate code if len(imgsetid_list) == 1: context_options += [ ( 'View imageset in Web', lambda: ibswgt.back.show_imgsetid_list_in_web(imgsetid_list), ), ( "Turk imageset's detections in Web", lambda: ibswgt.back.show_imgsetid_detection_turk_in_web( imgsetid_list ), ), ( "Turk imageset's annotations in Web", lambda: ibswgt.back.show_imgsetid_annotation_turk_in_web( imgsetid_list ), ), ('----', lambda: None), ( 'Run detection on imageset (can cause duplicates)', lambda: ibswgt.back.run_detection_on_imageset(imgsetid_list), ), ( 'Merge %d imageset into %s' % (len(imgsetid_list), (imagesettext)), lambda: ibswgt.back.merge_imagesets( imgsetid_list, merge_destination_id ), ), ('Copy imageset', lambda: ibswgt.back.copy_imageset(imgsetid_list)), ( 'Export imageset', lambda: ibswgt.back.export_imagesets(imgsetid_list), ), ('----', lambda: None), ( 'Delete imageset', lambda: ibswgt.back.delete_imageset(imgsetid_list), ), ('----', lambda: None), ( 'Delete imageset AND images', lambda: ibswgt.back.delete_imageset_and_images(imgsetid_list), ), ('----', lambda: None), ( 'Mark imageset as Shipped to WildBook', lambda: ibswgt.back.mark_imageset_as_shipped(imgsetid_list), ), ] else: context_options += [ ( 'Run detection on imagesets (can cause duplicates)', lambda: ibswgt.back.run_detection_on_imageset(imgsetid_list), ), ('Copy imageset', lambda: ibswgt.back.copy_imageset(imgsetid_list)), ( 'Merge %d imagesets into %s' % (len(imgsetid_list), (imagesettext)), lambda: ibswgt.back.merge_imagesets( imgsetid_list, merge_destination_id ), ), ('----', lambda: None), ( 'Delete imagesets', lambda: ibswgt.back.delete_imageset(imgsetid_list), ), ('----', lambda: None), ( 'Delete imagesets AND images', lambda: ibswgt.back.delete_imageset_and_images(imgsetid_list), ), # ('export imagesets', lambda: ibswgt.back.export_imagesets(imgsetid_list)), ] # ---- IMAGE CONTEXT ---- elif model.name == IMAGE_TABLE: gid_list = level2_ids[0] imgsetid = ibswgt.back.get_selected_imgsetid() context_options += build_image_context_options( ibswgt, ibs, gid_list, imgsetid ) # ---- IMAGE GRID CONTEXT ---- elif model.name == IMAGE_GRID: gid_list = level2_ids[0] imgsetid = ibswgt.back.get_selected_imgsetid() context_options += build_image_context_options( ibswgt, ibs, gid_list, imgsetid, goto_image_in_imgtbl=True ) # ---- ANNOTATION CONTEXT ---- elif model.name == gh.ANNOTATION_TABLE: aid_list = level2_ids[0] # Conditional context menu # TODO: UNIFY COMMMON CONTEXT MENUS context_options += build_annot_context_options( ibswgt, ibs, aid_list, model.imgsetid, goto_annot=False ) # ---- NAMES TREE CONTEXT ---- elif model.name == NAMES_TREE: # TODO: map level list to tablename more reliably ut.print_dict(level2_ids) nid_list = level2_ids.get(0, []) aid_list = level2_ids.get(1, []) if len(aid_list) > 0 and len(nid_list) > 0: # two types of indices are selected, just return # fixme to do something useful print('multiple types of indicies selected') return else: imgsetid = model.imgsetid context_options += name_context_options( ibswgt, ibs, nid_list, aid_list, imgsetid ) # Show the context menu # ut.print_list(context_options, nl=2) if len(context_options) > 0: gt.popup_menu(tblview, pos, context_options)
[docs] @slot_(QtCore.QModelIndex) def on_doubleclick(ibswgt, qtindex): """ Double clicking anywhere in the GUI CommandLine: python -m wbia --db lynx --imgsetid 2 --select-name=goku """ print('\n+--- DOUBLE CLICK ---') if not qtindex.isValid(): print('[doubleclick] invalid qtindex') return model = qtindex.model() id_ = model._get_row_id(qtindex) if model.name == IMAGESET_TABLE: imgsetid = id_ ibswgt.select_imageset_tab(imgsetid) else: imgsetid = model.imgsetid if (model.name == IMAGE_TABLE) or (model.name == IMAGE_GRID): gid = id_ ibswgt.spawn_edit_image_annotation_interaction( model, qtindex, gid, imgsetid ) elif model.name == gh.ANNOTATION_TABLE: aid = id_ ibswgt.back.select_aid(aid, imgsetid) elif model.name == NAME_TABLE: nid = id_ ibswgt.back.select_nid(nid, imgsetid) elif model.name == NAMES_TREE: level = model._get_level(qtindex) if level == 0: nid = id_ ibswgt.back.select_nid(nid, imgsetid, show=True) elif level == 1: aid = id_ ibswgt.spawn_edit_image_annotation_interaction_from_aid( aid, imgsetid, model, qtindex )
# ibswgt.back.select_aid(aid, imgsetid, show=True) # @slot_(list)
[docs] def imagesDropped(ibswgt, url_list): r""" image drag and drop event CommandLine: python -m wbia.gui.newgui imagesDropped --show Example: >>> # xdoctest: +REQUIRES(--gui) >>> from wbia.gui.newgui import * # NOQA >>> ibs, back, ibswgt, testdata_main_loop = testdata_guifront('hstest') >>> url_list = [ut.grab_test_imgpath('carl.jpg'), ut.grab_test_imgpath('lena.png')] >>> #url_list += [ut.truepath('~/Downloads/Clutter/wd_peter2.zip')] >>> url = url_list[0] >>> ut.quit_if_noshow() >>> ibswgt.imagesDropped(url_list) >>> testdata_main_loop(globals(), locals()) """ print('[drop_event] url_list=%r' % (url_list,)) has_zipext = ut.partial(ut.fpath_has_ext, exts=['.zip']) gpath_list = list(filter(ut.fpath_has_imgext, url_list)) dir_list = list(filter(isdir, url_list)) zipfile_list = list(filter(has_zipext, url_list)) old = False if old: if len(dir_list) > 0: options = ['No', 'Yes'] title = 'Non-Images dropped' msg = 'Recursively import from directories?' ans = gt.user_option(ibswgt, msg=msg, title=title, options=options) if ans == 'Yes': unflat_gpaths = [ ut.list_images(dir_, fullpath=True, recursive=True) for dir_ in dir_list ] flat_gpaths = ut.flatten(unflat_gpaths) flat_unix_gpaths = list(map(ut.unixpath, flat_gpaths)) gpath_list.extend(flat_unix_gpaths) else: return print('[drop_event] gpath_list=%r' % (gpath_list,)) if len(gpath_list) > 0: ibswgt.back.import_images_from_file(gpath_list=gpath_list) else: from wbia.dbio import ingest_database ibs = ibswgt.back.ibs ingestable = ingest_database.Ingestable2( ibs.get_dbdir(), gpath_list, dir_list, zipfile_list ) num_gpaths = len(ingestable.imgpath_list) num_dpaths = len(ingestable.imgdir_list) num_zips = len(ingestable.zipfile_list) confirm_list = [] if num_gpaths > 0: confirm_list += [ut.quantstr('image file', num_gpaths)] if num_dpaths > 0: confirm_list += [ 'recursively from ' + ut.quantstr('directory', num_dpaths, 's') ] if num_zips > 0: confirm_list += [ut.quantstr('zip file', num_zips, 's')] confirm_msg = 'Import from: ' + ut.conj_phrase(confirm_list, 'and') + '.' # gt.rrrr() config = ingestable.ingest_config # cfg = config dlg = gt.ConfigConfirmWidget.as_dialog( ibswgt, title='Confirm Import Images', msg=confirm_msg, config=config ) dlg.resize(700, 500) self = dlg.widget dlg.exec_() print('config = %r' % (config,)) updated_config = self.config # NOQA print('updated_config = %r' % (updated_config,)) gid_list = ingestable.execute(ibs=ibs) ibswgt.back._process_new_images( refresh=True, gid_list=gid_list, clock_offset=False )
[docs] def select_table_id(ibswgt, table_key, level, id_, imgsetid): select_func_dict = { (IMAGE_TABLE, 0): ibswgt.back.select_gid, (IMAGE_GRID, 0): ibswgt.back.select_gid, (gh.ANNOTATION_TABLE, 0): ibswgt.back.select_aid, (NAME_TABLE, 0): ibswgt.back.select_nid, (NAMES_TREE, 0): ibswgt.back.select_nid, (NAMES_TREE, 1): ibswgt.back.select_aid, } select_func = select_func_dict[(table_key, level)] select_func(id_, imgsetid, show=False)
[docs] def edit_image_time(ibswgt, gid_list): """ CommandLine: python -m wbia.gui.newgui --exec-edit_image_time --show Example: >>> # DISABLE_DOCTEST >>> from wbia.gui.newgui import * # NOQA >>> ibs, back, ibswgt, testdata_main_loop = testdata_guifront('testdb3') >>> #ibs, back, ibswgt, testdata_main_loop = testdata_guifront('lynx') >>> ibswgt.edit_image_time([277, 630]) >>> testdata_main_loop(globals(), locals()) """ from wbia.gui import clock_offset_gui ibswgt.co_wgt = clock_offset_gui.ClockOffsetWidget( ibswgt.ibs, gid_list, hack=True ) ibswgt.co_wgt.show()
[docs] def filter_annotation_table(ibswgt): r""" TODO: Finish implementation CommandLine: python -m wbia.gui.newgui --test-filter_annotation_table --show --db lynx --imgsetid 2 Example: >>> # DISABLE_DOCTEST >>> from wbia.gui.newgui import * # NOQA >>> ibs, back, ibswgt, testdata_main_loop = testdata_guifront('testdb3') >>> #ibs, back, ibswgt, testdata_main_loop = testdata_guifront('PZ_Master1') >>> result = ibswgt.filter_annotation_table() >>> print(result) >>> testdata_main_loop(globals(), locals()) """ from functools import partial # ibs.filter_annots_general() ibs = ibswgt.back.ibs # ibs.filterannots_by_tags(aid_list) print('\n------FILTERING ANNOTS\n\n') # annotmatch_rowid_list = ibs._get_all_annotmatch_rowids() # isscenerymatch_list = ibs.get_annotmatch_is_scenerymatch(annotmatch_rowid_list) # ut.take(isscenerymatch_list, ut.list_where(isscenerymatch_list)) # Applies annotation based filtering to the annotation table # filter_kw = dict(any_matches='.*error.*', been_adjusted=True) filter_kw = dict(been_adjusted=True) # filter_kw = dict(require_timestamp=True) filter_fn = partial(ibs.filter_annots_general, filter_kw=filter_kw) model = ibswgt.models[gh.ANNOTATION_TABLE] # NOQA model.set_ider_filters([filter_fn]) with ChangeLayoutContext([model]): model._update_rows(rebuild_structure=True)
###################### ###### Testing ####### ######################
[docs]def testdata_guifront(defaultdb='testdb1'): import wbia main_locals = wbia.main(defaultdb=defaultdb, allow_newdir=True) ibs, back = ut.dict_take(main_locals, ['ibs', 'back']) ibswgt = back.ibswgt # NOQA globals__ = globals() locals__ = locals() def testdata_main_loop(globals_=globals__, locals_=locals__): locals_ = locals_.copy() globals_ = globals_.copy() locals_.update(locals__) globals_.update(globals__) if '--cmd' in sys.argv: gt.qtapp_loop(qwin=ibswgt, ipy=True) six.exec_(ut.ipython_execstr(), globals_, locals_) elif ut.show_was_requested(): gt.qtapp_loop(qwin=ibswgt) return ibs, back, ibswgt, testdata_main_loop
[docs]def testfunc(): r""" CommandLine: python -m wbia.gui.newgui --test-testfunc --show python -m wbia.gui.newgui --test-testfunc --cmd Example: >>> # DISABLE_DOCTEST >>> from wbia.gui.newgui import * # NOQA >>> result = testfunc() >>> print(result) """ ibs, back, ibswgt, testdata_main_loop = testdata_guifront() view = ibswgt.views[gh.IMAGE_TABLE] testdata_main_loop(globals(), locals())
if __name__ == '__main__': """ CommandLine: python -m wbia.gui.newgui python -m wbia.gui.newgui --allexamples python -m wbia.gui.newgui --allexamples --noface --nosrc """ import multiprocessing multiprocessing.freeze_support() # for win32 import utool as ut # NOQA ut.doctest_funcs()