def __init__(self, image_list, parent=None): super(PhotiniMap, self).__init__(parent) self.app = QtWidgets.QApplication.instance() self.image_list = image_list self.geocode_cache = OrderedDict() name = self.__class__.__name__.lower() self.api_key = key_store.get(name, 'api_key') self.search_key = key_store.get('opencage', 'api_key') self.script_dir = pkg_resources.resource_filename( 'photini', 'data/' + name + '/') self.drag_icon = QtGui.QPixmap( os.path.join(self.script_dir, '../map_pin_grey.png')) self.drag_hotspot = 10, 35 self.search_string = None self.map_loaded = False self.marker_info = {} self.map_status = {} self.dropped_images = [] self.setChildrenCollapsible(False) left_side = QtWidgets.QWidget() self.addWidget(left_side) left_side.setLayout(QtWidgets.QFormLayout()) left_side.layout().setContentsMargins(0, 0, 0, 0) left_side.layout().setFieldGrowthPolicy( QtWidgets.QFormLayout.AllNonFixedFieldsGrow) # map self.map = WebView() self.map.setPage(WebPage(parent=self.map)) self.call_handler = CallHandler(parent=self) if QtWebEngineWidgets: self.web_channel = QtWebChannel.QWebChannel(parent=self) self.map.page().setWebChannel(self.web_channel) self.web_channel.registerObject('python', self.call_handler) self.map.settings().setAttribute( WebSettings.Accelerated2dCanvasEnabled, False) else: self.map.page().setLinkDelegationPolicy( QtWebKitWidgets.QWebPage.DelegateAllLinks) self.map.page().linkClicked.connect(self.link_clicked) self.map.page().mainFrame().javaScriptWindowObjectCleared.connect( self.java_script_window_object_cleared) self.map.settings().setAttribute( WebSettings.LocalContentCanAccessRemoteUrls, True) self.map.settings().setAttribute( WebSettings.LocalContentCanAccessFileUrls, True) self.map.setAcceptDrops(False) self.map.drop_text.connect(self.drop_text) self.addWidget(self.map) # search search_layout = QtWidgets.QFormLayout() search_layout.setContentsMargins(0, 0, 0, 0) search_layout.setVerticalSpacing(0) search_layout.setFieldGrowthPolicy( QtWidgets.QFormLayout.AllNonFixedFieldsGrow) self.edit_box = ComboBox() self.edit_box.setEditable(True) self.edit_box.setInsertPolicy(QtWidgets.QComboBox.NoInsert) self.edit_box.lineEdit().setPlaceholderText( translate('PhotiniMap', '<new search>')) self.edit_box.lineEdit().returnPressed.connect(self.search) self.edit_box.activated.connect(self.goto_search_result) self.clear_search() self.edit_box.setEnabled(False) search_layout.addRow(translate('PhotiniMap', 'Search'), self.edit_box) # search terms and conditions terms = self.search_terms() if terms: search_layout.addRow(*terms) left_side.layout().addRow(search_layout) if terms: divider = QtWidgets.QFrame() divider.setFrameStyle(QtWidgets.QFrame.HLine) left_side.layout().addRow(divider) left_side.layout().addItem( QtWidgets.QSpacerItem(1, 1000, vPolicy=QtWidgets.QSizePolicy.Expanding)) # latitude & longitude layout = QtWidgets.QHBoxLayout() self.coords = SingleLineEdit() self.coords.editingFinished.connect(self.new_coords) self.coords.setEnabled(False) layout.addWidget(self.coords) # convert lat/lng to location info self.auto_location = QtWidgets.QPushButton( translate('PhotiniMap', six.unichr(0x21e8) + ' address')) self.auto_location.setFixedHeight(self.coords.height()) self.auto_location.setEnabled(False) self.auto_location.clicked.connect(self.get_address) layout.addWidget(self.auto_location) left_side.layout().addRow(translate('PhotiniMap', 'Lat, long'), layout) # location info self.location_widgets = [] self.location_info = QtWidgets.QTabWidget() tab_bar = QTabBar() self.location_info.setTabBar(tab_bar) tab_bar.context_menu.connect(self.location_tab_context_menu) tab_bar.tabMoved.connect(self.location_tab_moved) self.location_info.setElideMode(Qt.ElideLeft) self.location_info.setMovable(True) self.location_info.setEnabled(False) left_side.layout().addRow(self.location_info) # address lookup (and default search) terms and conditions layout = QtWidgets.QHBoxLayout() if terms: widget = CompactButton( self.tr('Address lookup\npowered by OpenCage')) else: widget = CompactButton( self.tr('Search && lookup\npowered by OpenCage')) widget.clicked.connect(self.load_tou_opencage) layout.addWidget(widget) widget = CompactButton( self.tr('Geodata © OpenStreetMap\ncontributors')) widget.clicked.connect(self.load_tou_osm) layout.addWidget(widget) left_side.layout().addRow(layout) # other init self.image_list.image_list_changed.connect(self.image_list_changed) self.splitterMoved.connect(self.new_split) self.block_timer = QtCore.QTimer(self) self.block_timer.setInterval(5000) self.block_timer.setSingleShot(True) self.block_timer.timeout.connect(self.enable_search)
def __init__(self, options, initial_files): super(MainWindow, self).__init__() self.setWindowTitle(self.tr("Photini photo metadata editor")) pixmap = QtGui.QPixmap() pixmap.loadFromData(pkg_resources.resource_string( 'photini', 'data/icons/48/photini.png')) icon = QtGui.QIcon(pixmap) self.setWindowIcon(icon) self.selection = list() # logger window self.loggerwindow = LoggerWindow(options.verbose) self.loggerwindow.setWindowIcon(icon) self.logger = logging.getLogger(self.__class__.__name__) # set network proxy proxies = getproxies() if 'http' in proxies: parsed = urlparse(proxies['http']) QNetworkProxy.setApplicationProxy(QNetworkProxy( QNetworkProxy.HttpProxy, parsed.hostname, parsed.port)) # create shared global objects self.app = QtWidgets.QApplication.instance() self.app.config_store = ConfigStore('editor', parent=self) self.app.spell_check = SpellCheck(parent=self) self.app.test_mode = options.test # restore size size = self.width(), self.height() self.resize(*eval( self.app.config_store.get('main_window', 'size', str(size)))) # image selector self.image_list = ImageList() self.image_list.selection_changed.connect(self.new_selection) self.image_list.new_metadata.connect(self.new_metadata) # prepare list of tabs and associated stuff self.tab_list = ( {'name' : self.tr('&Descriptive metadata'), 'key' : 'descriptive_metadata', 'class' : Descriptive}, {'name' : self.tr('&Technical metadata'), 'key' : 'technical_metadata', 'class' : Technical}, {'name' : self.tr('Map (&Google)'), 'key' : 'map_google', 'class' : GoogleMap}, {'name' : self.tr('Map (&Bing)'), 'key' : 'map_bing', 'class' : BingMap}, {'name' : self.tr('Map (&OSM)'), 'key' : 'map_osm', 'class' : OpenStreetMap}, {'name' : self.tr('&Flickr upload'), 'key' : 'flickr_upload', 'class' : FlickrUploader}, {'name' : self.tr('Google &Photos upload'), 'key' : 'picasa_upload', 'class' : PicasaUploader}, {'name' : self.tr('Faceboo&k upload'), 'key' : 'facebook_upload', 'class' : FacebookUploader}, {'name' : self.tr('&Import photos'), 'key' : 'import_photos', 'class' : Importer}, ) for tab in self.tab_list: if tab['class']: tab['object'] = tab['class'](self.image_list) else: tab['object'] = None # file menu file_menu = self.menuBar().addMenu(self.tr('File')) open_action = QtWidgets.QAction(self.tr('Open images'), self) open_action.setShortcuts(QtGui.QKeySequence.Open) open_action.triggered.connect(self.image_list.open_files) file_menu.addAction(open_action) self.save_action = QtWidgets.QAction( self.tr('Save images with new data'), self) self.save_action.setShortcuts(QtGui.QKeySequence.Save) self.save_action.setEnabled(False) self.save_action.triggered.connect(self.image_list.save_files) file_menu.addAction(self.save_action) self.close_action = QtWidgets.QAction( self.tr('Close selected images'), self) self.close_action.setEnabled(False) self.close_action.triggered.connect(self.close_files) file_menu.addAction(self.close_action) close_all_action = QtWidgets.QAction(self.tr('Close all images'), self) close_all_action.triggered.connect(self.close_all_files) file_menu.addAction(close_all_action) file_menu.addSeparator() quit_action = QtWidgets.QAction(self.tr('Quit'), self) quit_action.setShortcuts( [QtGui.QKeySequence.Quit, QtGui.QKeySequence.Close]) quit_action.triggered.connect( QtWidgets.QApplication.instance().closeAllWindows) file_menu.addAction(quit_action) # options menu options_menu = self.menuBar().addMenu(self.tr('Options')) settings_action = QtWidgets.QAction(self.tr('Settings'), self) settings_action.triggered.connect(self.edit_settings) options_menu.addAction(settings_action) options_menu.addSeparator() for tab in self.tab_list: name = tab['name'].replace('&', '') tab['action'] = QtWidgets.QAction(name, self) tab['action'].setCheckable(True) if tab['class']: tab['action'].setChecked( eval(self.app.config_store.get('tabs', tab['key'], 'True'))) else: tab['action'].setEnabled(False) tab['action'].triggered.connect(self.add_tabs) options_menu.addAction(tab['action']) # spelling menu languages = self.app.spell_check.available_languages() spelling_menu = self.menuBar().addMenu(self.tr('Spelling')) enable_action = QtWidgets.QAction(self.tr('Enable spell check'), self) enable_action.setEnabled(bool(languages)) enable_action.setCheckable(True) enable_action.setChecked(self.app.spell_check.enabled) enable_action.toggled.connect(self.app.spell_check.enable) spelling_menu.addAction(enable_action) language_menu = QtWidgets.QMenu(self.tr('Choose language'), self) language_menu.setEnabled(bool(languages)) language_group = QtWidgets.QActionGroup(self) current_language = self.app.spell_check.current_language() for tag in languages: language_action = QtWidgets.QAction(tag, self) language_action.setCheckable(True) language_action.setChecked(tag == current_language) language_action.setActionGroup(language_group) language_menu.addAction(language_action) language_group.triggered.connect(self.app.spell_check.set_language) spelling_menu.addMenu(language_menu) # help menu help_menu = self.menuBar().addMenu(self.tr('Help')) about_action = QtWidgets.QAction(self.tr('About Photini'), self) about_action.triggered.connect(self.about) help_menu.addAction(about_action) help_menu.addSeparator() help_action = QtWidgets.QAction(self.tr('Photini documentation'), self) help_action.triggered.connect(self.open_docs) help_menu.addAction(help_action) # main application area self.central_widget = QtWidgets.QSplitter() self.central_widget.setOrientation(Qt.Vertical) self.central_widget.setChildrenCollapsible(False) self.tabs = QtWidgets.QTabWidget() self.tabs.setTabBar(QTabBar()) self.tabs.setElideMode(Qt.ElideRight) self.tabs.currentChanged.connect(self.new_tab) self.add_tabs() self.central_widget.addWidget(self.tabs) self.central_widget.addWidget(self.image_list) size = self.central_widget.sizes() self.central_widget.setSizes(eval( self.app.config_store.get('main_window', 'split', str(size)))) self.central_widget.splitterMoved.connect(self.new_split) self.setCentralWidget(self.central_widget) # open files given on command line, after GUI is displayed self.initial_files = initial_files if self.initial_files: QtCore.QTimer.singleShot(0, self.open_initial_files)
def __init__(self, options, initial_files): super(MainWindow, self).__init__() self.setWindowTitle( translate('MenuBar', "Photini photo metadata editor")) pixmap = QtGui.QPixmap() pixmap.loadFromData( pkg_resources.resource_string('photini', 'data/icons/photini_48.png')) icon = QtGui.QIcon(pixmap) self.setWindowIcon(icon) self.selection = list() # logger window self.loggerwindow = LoggerWindow(options.verbose) self.loggerwindow.setWindowIcon(icon) # set network proxy proxies = urllib.request.getproxies() if 'http' in proxies: parsed = urllib.parse.urlparse(proxies['http']) QNetworkProxy.setApplicationProxy( QNetworkProxy(QNetworkProxy.HttpProxy, parsed.hostname, parsed.port)) # create shared global objects self.app = QtWidgets.QApplication.instance() self.app.config_store = ConfigStore('editor', parent=self) self.app.spell_check = SpellCheck(parent=self) self.app.open_cage = OpenCage(parent=self) self.app.options = options # restore size size = self.width(), self.height() self.resize( *eval(self.app.config_store.get('main_window', 'size', str(size)))) # image selector self.image_list = ImageList() self.image_list.selection_changed.connect(self.new_selection) self.image_list.new_metadata.connect(self.new_metadata) # update config file if self.app.config_store.config.has_section('tabs'): conv = { 'descriptive_metadata': 'photini.descriptive', 'technical_metadata': 'photini.technical', 'map_google': 'photini.googlemap', 'map_bing': 'photini.bingmap', 'map_mapbox': 'photini.mapboxmap', 'map_osm': 'photini.openstreetmap', 'address': 'photini.address', 'flickr_upload': 'photini.flickr', 'import_photos': 'photini.importer', } for key in self.app.config_store.config.options('tabs'): if key in conv: self.app.config_store.set( 'tabs', conv[key], self.app.config_store.get('tabs', key)) self.app.config_store.config.remove_option('tabs', key) # prepare list of tabs and associated stuff self.tab_list = [] default_modules = [ 'photini.descriptive', 'photini.technical', 'photini.googlemap', 'photini.bingmap', 'photini.mapboxmap', 'photini.openstreetmap', 'photini.address', 'photini.flickr', 'photini.googlephotos', 'photini.importer' ] modules = eval( self.app.config_store.get('tabs', 'modules', pprint.pformat(default_modules))) for n, module in enumerate(default_modules): if module not in modules: modules = list(modules) modules.insert(n, module) self.app.config_store.set('tabs', 'modules', pprint.pformat(modules)) for module in modules: tab = {'module': module} try: mod = importlib.import_module(tab['module']) tab['class'] = mod.TabWidget tab['name'] = tab['class'].tab_name() except ImportError as ex: print(str(ex)) tab['class'] = None self.tab_list.append(tab) # file menu file_menu = self.menuBar().addMenu(translate('MenuBar', 'File')) action = file_menu.addAction(translate('MenuBar', 'Open files')) action.setShortcuts(QtGui.QKeySequence.Open) action.triggered.connect(self.image_list.open_files) self.save_action = file_menu.addAction( translate('MenuBar', 'Save changes')) self.save_action.setShortcuts(QtGui.QKeySequence.Save) self.save_action.setEnabled(False) self.save_action.triggered.connect(self.image_list.save_files) action = file_menu.addAction(translate('MenuBar', 'Close all files')) action.triggered.connect(self.image_list.close_all_files) sep = file_menu.addAction(translate('MenuBar', 'Selected images')) sep.setSeparator(True) self.selected_actions = self.image_list.add_selected_actions(file_menu) if GpxImporter: self.import_gpx_action = file_menu.addAction( translate('MenuBar', 'Import GPX file')) self.import_gpx_action.triggered.connect(self.import_pgx_file) else: self.import_gpx_action = None file_menu.addSeparator() action = file_menu.addAction(translate('MenuBar', 'Quit')) action.setShortcuts( [QtGui.QKeySequence.Quit, QtGui.QKeySequence.Close]) action.triggered.connect( QtWidgets.QApplication.instance().closeAllWindows) # options menu options_menu = self.menuBar().addMenu(translate('MenuBar', 'Options')) action = options_menu.addAction(translate('MenuBar', 'Settings')) action.triggered.connect(self.edit_settings) options_menu.addSeparator() for tab in self.tab_list: if tab['class']: name = tab['name'].replace('&', '') else: name = tab['module'] tab['action'] = options_menu.addAction(name) tab['action'].setCheckable(True) if tab['class']: tab['action'].setChecked( eval( self.app.config_store.get('tabs', tab['module'], 'True'))) else: tab['action'].setEnabled(False) tab['action'].triggered.connect(self.add_tabs) # spelling menu languages = self.app.spell_check.available_languages() spelling_menu = self.menuBar().addMenu(translate( 'MenuBar', 'Spelling')) action = spelling_menu.addAction( translate('MenuBar', 'Enable spell check')) action.setEnabled(languages is not None) action.setCheckable(True) action.setChecked(self.app.spell_check.enabled) action.toggled.connect(self.app.spell_check.enable) language_menu = spelling_menu.addMenu( translate('MenuBar', 'Choose language')) language_menu.setEnabled(languages is not None) current_language = self.app.spell_check.current_language() if languages: language_group = QtWidgets.QActionGroup(self) for name, code in languages: if name != code: name = code + ': ' + name action = language_menu.addAction(name) action.setCheckable(True) action.setChecked(code == current_language) action.setData(code) action.setActionGroup(language_group) language_group.triggered.connect(self.set_language) else: action = language_menu.addAction( translate('MenuBar', 'No dictionary installed')) action.setEnabled(False) # help menu help_menu = self.menuBar().addMenu(translate('MenuBar', 'Help')) action = help_menu.addAction(translate('MenuBar', 'About Photini')) action.triggered.connect(self.about) help_menu.addSeparator() action = help_menu.addAction( translate('MenuBar', 'Photini documentation')) action.triggered.connect(self.open_docs) # main application area self.central_widget = QtWidgets.QSplitter() self.central_widget.setOrientation(Qt.Vertical) self.central_widget.setChildrenCollapsible(False) self.tabs = QtWidgets.QTabWidget() self.tabs.setTabBar(QTabBar()) self.tabs.setElideMode(Qt.ElideRight) self.tabs.currentChanged.connect(self.new_tab) self.add_tabs(False) self.central_widget.addWidget(self.tabs) self.central_widget.addWidget(self.image_list) size = self.central_widget.sizes() self.central_widget.setSizes( eval(self.app.config_store.get('main_window', 'split', str(size)))) self.central_widget.splitterMoved.connect(self.new_split) self.setCentralWidget(self.central_widget) # open files given on command line, after GUI is displayed self.initial_files = initial_files if self.initial_files: QtCore.QTimer.singleShot(0, self.open_initial_files)