示例#1
0
	def get_sql(self, query, vars=None):
		"""获取编译后的sql语句"""
		# 记录程序执行开始时间
		start_time = datetime.now().timestamp()#time.clock()
		try:
			# 判断是否记录sql执行语句
			if self.is_output_sql:
				log_helper.info('sql:' + str(query))
			# 建立游标
			self.cursor = self.connect.cursor()
			# 执行SQL
			self.data = self.cursor.mogrify(query, vars)
		except Exception as e:
			# 将异常写入到日志中
			log_helper.error('sql生成失败:' + str(e.args) + ' query:' + str(query))
			self.data = '获取编译sql失败'
		finally:
			# 关闭游标
			self.cursor.close()
		# 记录程序执行结束时间
		end_time = datetime.now().timestamp()
		# 写入日志
		self.write_log(start_time, end_time, query)

		return self.data
示例#2
0
文件: common.py 项目: yani2012/Order9
def pickle_dump(file, data):
    #with保证自动关闭文件
    #设置文件模式为'wb'来以二进制写模式打开文件
    with open(file, 'wb') as f:
        #dump()函数接受一个可序列化的Python数据结构
        pickle.dump(data, f)
        log_helper.info('[info]pickle_dump success')
    def reader_loading_finished(self, loaded_zoom_level, loaded_extent):
        self._loaded_extent = loaded_extent
        if self.progress_dialog:
            self.progress_dialog.hide()

        auto_zoom = self._auto_zoom

        self._loaded_scale = self._get_current_map_scale()
        self.refresh_layers()
        info("Loading of zoom level {} complete! Loaded extent: {}",
             loaded_zoom_level, loaded_extent)
        if loaded_extent and (not auto_zoom or
                              (auto_zoom and self._loaded_scale is None)):
            scheme = self._current_reader.source.scheme()
            visible_extent = self._get_visible_extent_as_tile_bounds(
                scheme, loaded_zoom_level)
            overlap = self._extent_overlap_bounds(visible_extent,
                                                  loaded_extent)
            if not overlap:
                self._set_qgis_extent(zoom=loaded_zoom_level,
                                      scheme=scheme,
                                      bounds=loaded_extent)

        self._is_loading = False
        if auto_zoom:
            self._debouncer.start()
示例#4
0
    def sellBasket(self, stkPriceVol):
        '''
        一篮子股票卖出
        stkPriceVol:pandas.DataFrame格式
        '''
        if len(stkPriceVol) == 0:
            return
        if self.broker == u'华泰证券':
            for i in range(len(stkPriceVol)):
                stk, price, vol = stkPriceVol.iloc[i, :]
                self.sell(stk, price, vol)
        elif self.broker == u'中信建投':
            for i in range(len(stkPriceVol)):
                stk, price, vol = stkPriceVol.iloc[i, :]
                self.sell(stk, price, vol)
        else:
            stks = stkPriceVol.iloc[:, 0].astype(str)
            prices = stkPriceVol.iloc[:, 1].astype(float).map(
                lambda x: round(x, 2))
            vols = stkPriceVol.iloc[:, 2].astype(int)
            categorys = [1] * len(stks)

            if (prices * vols).max() > self.riskLimit:
                log_helper.error('[error]sellBasket: money out of risklimit')
                return

            self._sendOrders(list(stks), list(prices), list(vols),
                             list(categorys))
        log_helper.info('[info]sellBasket:{0}'.format(stkPriceVol))
示例#5
0
    def cancel(self, stk, direction='buy', method='all'):
        log_helper.info('cancel[stock:{0},direction:{1}]'.format(
            stk, direction))
        result = '\0' * 256
        errInf = '\0' * 256
        dir_id = 0 if direction == 'buy' else 1
        exchangeID = '1' if stk[0] == '6' else '0'
        entru = self.query_active_orders()
        if entru is None:
            return
        hth = entru.loc[(entru['StockCode'] == stk) &
                        (entru['Direction'] == dir_id),
                        ['EntruId', 'EntruTime']]  #合同编号
        hth.sort_values('EntruTime', inplace=True)

        if len(hth) == 0:
            log_helper.error('[error]请检查挂单是否存在')
            return

        if method == 'all':
            for entruId in hth['EntruId']:
                log_helper.info(entruId)
                self._cancel_byID(exchangeID, entruID, result, errInf)
        elif method == 'first':
            entruId = str(hth['EntruId'].iloc[0])
            self._cancel_byID(exchangeID, entruID, result, errInf)
        elif method == 'last':
            entruId = str(hth['EntruId'].iloc[-1])
            self._cancel_byID(exchangeID, entruID, result, errInf)
    def _on_add_layer(self, path_or_url, selected_layers):
        assert path_or_url

        crs_string = self._current_reader.source.crs()
        self._init_qgis_map(crs_string)

        scheme = self._current_reader.source.scheme()
        zoom = self._get_current_zoom()

        extent = self._get_visible_extent_as_tile_bounds(scheme=scheme,
                                                         zoom=zoom)

        bounds = self._current_reader.source.bounds_tile(zoom)
        info("Bounds of source: {}", bounds)
        is_within_bounds = self.is_extent_within_bounds(extent, bounds)
        if not is_within_bounds:
            # todo: set the current QGIS map extent inside the available bounds of the source
            pass

        if not self._is_valid_qgis_extent(extent_to_load=extent, zoom=zoom):
            extent = self._current_reader.source.bounds_tile(zoom)

        keep_dialog_open = self.connections_dialog.keep_dialog_open()
        if keep_dialog_open:
            dialog_owner = self.connections_dialog
        else:
            dialog_owner = self.iface.mainWindow()
            self.connections_dialog.close()
        self._create_progress_dialog(dialog_owner, on_cancel=self._cancel_load)
        self._load_tiles(options=self.connections_dialog.options,
                         layers_to_load=selected_layers,
                         bounds=extent)
        self._current_layer_filter = selected_layers
示例#7
0
 def _logOff(self):
     #登出帐号
     if self.is_tradex:
         TradeX.Logoff(self.client)
     else:
         self.dll.Logoff(self.client)
     log_helper.info('[info]user %s log off' % self.username)
示例#8
0
 def _queryData(self, category=0):
     '''
     查询各类交易数据
    Category:表示查询信息的种类,
        0资金  
        1股份   
        2当日委托  
        3当日成交     
        4可撤单   
        5股东代码  
        6融资余额   
        7融券余额  
        8可融证券   
     '''
     self.query_count += 1
     log_helper.info('query data type:{0} for account:{1}'.format(
         category, self.username))
     result = '\0' * 1024 * 1024
     errInf = '\0' * 256
     if self.is_tradex:
         errInf, result = self.client.QueryData(category)
     else:
         self.dll.QueryData(self.client, category, result, errInf)
     #log_helper.info('errorInt:{0}, result:{1}'.format(errInf, result.rstrip('\0')))
     result = result.rstrip('\0')
     #        print(errInf.decode("gbk"))
     result_gbk = result.decode("gbk")
     return (result_gbk)
 def _is_valid_qgis_extent(self, extent_to_load, zoom):
     source_bounds = self._current_reader.source.bounds_tile(zoom)
     info("bounds: {}", source_bounds)
     if not source_bounds["x_min"] <= extent_to_load["x_min"] <= source_bounds["x_max"] \
             and not source_bounds["x_min"] <= extent_to_load["x_max"] <= source_bounds["x_max"] \
             and not source_bounds["y_min"] <= extent_to_load["y_min"] <= source_bounds["y_max"] \
             and not source_bounds["y_min"] <= extent_to_load["y_max"] <= source_bounds["y_max"]:
         return False
     return True
示例#10
0
 def has_sell_item(self, key, pool_item_dict):   #将卖出条件加入列表
     ret = False
     for item in itertools.chain.from_iterable(list (pool_item_dict.values())):
         #log_helper.info('itemkey:{0},new_key:{1} compare'.format(item.get_key(), key))
         if key == item.get_key():
             log_helper.info('itemkey:{0},new_key:{1} equal'.format(item.get_key(), key))
             ret = True
             break
     return ret
    def merge_features(self, layer):
        layer_name = layer.name()

        info("Merging features of layer: {}".format(layer_name))

        self._prepare_features_for_dissolvment(layer)
        dissolved_layer = self._dissolve(layer)

        return dissolved_layer
示例#12
0
def sell_work(context,
              data_cache_manager,
              pool_item_cache,
              pool_name,
              trigger_condition_check,
              mdHelper=None):
    for tempUser in pool_item_cache:
        poolItemList = list(pool_item_cache[tempUser].values())
        for poolItem in itertools.chain.from_iterable(poolItemList):
            if poolItem.is_sent_order:
                continue
            vol = int(poolItem.vol)
            marketData = context.market_depths_df.ix[poolItem.stock]
            last_px = marketData['last_px']
            if last_px <= 0:
                continue

            need_trigger = False
            pre_close = marketData['pre_close']
            if not trigger_condition_check(
                    poolItem, marketData, mdHelper=mdHelper):
                continue

            positions = data_cache_manager.accounts[tempUser][
                'position'].set_index('StockCode')
            if poolItem.stock not in positions.index:
                log_helper.warn('no position on {0} when stop loss'.format(
                    poolItem.stock))
                continue

            available_sell_vol = positions['CoverableSize'][poolItem.stock]
            if available_sell_vol == 0:
                log_helper.warn('0 position on {0}  when stop loss'.format(
                    poolItem.stock))
                continue
            if vol > available_sell_vol > 0:
                vol = available_sell_vol

            if poolItem.waySell == u'追五挂单':
                order_price = common.get_bid5_price(marketData)
                poolItem.is_sent_order = True
                state = data_cache_manager.accounts[tempUser]['tradeApi'].sell(
                    poolItem.stock, order_price, vol)
                log_helper.info(
                    '[user:{0}][{1} sell stock({2}) at price:{3}]@{4}. state:{5}'
                    .format(tempUser, pool_name, poolItem.stock, order_price,
                            vol, state))
                state = state.splitlines()
                state = [i.split('\t') for i in state]
                if len(state) > 1:
                    poolItem.state = str(
                        pd.DataFrame(state[1:], columns=state[0]).iloc[0, 0])
示例#13
0
文件: common.py 项目: yani2012/Order9
def get_reverse_repo(position_df):
    if position_df is None:
        return 0.0
    reverse_repo = 0.0
    len_position_df = len(position_df)
    if len_position_df > 0:
        for i in range(len_position_df):
            rowData = position_df.iloc[i]
            if rowData['CostValue'] == 0.0 and rowData['MarketValue'] > 0:
                reverse_repo = rowData['MarketValue']
                log_helper.info('get reverse_repo:{0}'.format(reverse_repo))
                continue
    return reverse_repo
示例#14
0
    def update_trade_count(self, user_name, order_id, order_state):
        log_helper.info('username:{0}, order_id:{1}, orderstate:{2}'.format(user_name, order_id, order_state))
        if order_state != u'已成':
            return

        order_key = common.get_order_key(user_name, order_id)
        if order_key in self.orderkeyset:
            log_helper.info('{0} has already counted'.format(order_key))
            return
        
        if order_key not in self.orderid2pooldict.keys():
            log_helper.info('order key[{0} not in orderid2pooldict]'.format(order_key))
            return
        pool_name = self.orderid2pooldict[order_key]
        poolkey = '{0}_{1}'.format(user_name, pool_name)
        if pool_name in self.pool2ordersdict.keys():
            if order_id in self.pool2ordersdict[pool_name].keys():
                self.pool2ordersdict[pool_name].pop(order_id)
    
        if poolkey not in self.pool2tradecount.keys():
            self.pool2tradecount[poolkey] = 1
        else:
            self.pool2tradecount[poolkey] += 1

        self.orderkeyset.add(order_key)
        log_helper.info('order_key:{0}, poolkey:{1}, tradecount:{2}'.format(order_key, poolkey, self.pool2tradecount[poolkey]))
示例#15
0
 def check_user_when_load(self, userName):
     log_helper.info('check_user_when_load for userName:{0}'.format(userName))
     if userName not in self.UpLimitBuyConditions.keys():
         self.UpLimitBuyConditions.update({userName:OrderedDict()})
     if userName not in self.UpLimitBuyConditions_1st.keys():
         self.UpLimitBuyConditions_1st.update({userName:OrderedDict()})
     if userName not in self.UpLimitBuyConditions_1stNew.keys():
         self.UpLimitBuyConditions_1stNew.update({userName:OrderedDict()})
     if userName not in self.stop_loss_dict.keys():
         self.stop_loss_dict.update({userName:OrderedDict()})
     if userName not in self.max_drawdown_dict.keys():
         self.max_drawdown_dict.update({userName:OrderedDict()})
     if userName not in self.pool2tradecount.keys():
         self.pool2tradecount.update({userName:{}})
示例#16
0
	def execute(self, query, vars=None):
		"""执行sql语句查询,返回结果集或影响行数"""
		if not query:
			return None
		# 记录程序执行开始时间
		start_time = datetime.now().timestamp()
		try:
			# 判断是否记录sql执行语句
			if self.is_output_sql:
				log_helper.info('sql:' + str(query))
			# 建立游标
			if self.connect:
				self.cursor=self.connect.cursor()
			else:
				self.open_conn()
				self.cursor=self.connect.cursor()
			# 执行SQL
			result = self.cursor.executescript(query)
			
		except Exception as e:
			# 将异常写入到日志中
			log_helper.error('sql执行失败:' + str(e.args) + ' query:' + str(query))
			self.data = None
		else:
			self.commit()
			# 获取数据
			#try:
				#self.col=[tuple[0] for tuple in self.cursor.description]
				#if self.cursor.description:
					# 在执行insert/update/delete等更新操作时,如果添加了returning,则读取返回数据组合成字典返回
					#self.data =self.cursor.fetchall() #self.data = [dict((self.cursor.description[i][0], value) for i, value in enumerate(row)) for row in self.cursor.fetchall()]
				#else:
					# 如果执行insert/update/delete等更新操作时没有添加returning,则返回影响行数,值为0时表时没有数据被更新
					#self.data = self.cursor.rowcount
				#self.commit()
			#except Exception as e:
				# 将异常写入到日志中
				#log_helper.error('数据获取失败:' + str(e.args) + ' query:' + str(query))
				#self.data = None
				#self.rollback()
		#finally:
			# 关闭游标
			#self.cursor.close()
		# 记录程序执行结束时间
		end_time = datetime.now().timestamp()
		# 写入日志
		self.write_log(start_time, end_time, query)

		# 如果有返回数据,则把该数据返回给调用者
		return self.data
示例#17
0
    def get_stock_list(self, market_no):
        nStart = 0

        errinfo, count, result = self.clientHq.GetSecurityList(market_no, nStart)
        if errinfo != "":
            log_helper.error(errinfo)
            return
           
        res = result.splitlines()
        for item in res:
            log_helper.info(item)
        res = [stock_code.split('\t')[0] for stock_code in res ]

        #log_helper.info(res[:1500])
        return res[1:]
示例#18
0
    def delete_user(self, userName):
        log_helper.info('delete user:{0}'.format(userName))
        if userName in self.accounts:
            self.accounts.pop(userName)
        if userName in self.stop_loss_dict:
            self.stop_loss_dict.pop(userName)
        if userName in self.max_drawdown_dict:
            self.max_drawdown_dict.pop(userName)
        if userName in self.UpLimitBuyConditions:
            self.UpLimitBuyConditions.pop(userName)
        if userName in self.UpLimitBuyConditions_1st:
            self.UpLimitBuyConditions_1st.pop(userName)

        if userName in self.UpLimitBuyConditions_1stNew:
            self.UpLimitBuyConditions_1stNew.pop(userName)            
示例#19
0
    def merge_features(self, layer):

        # return layer, None

        layer_name = layer.name()
        # todo: remove after testing
        if layer_name.split("_")[0] not in ["forest", "lake", "residential"]:
            return layer

        info("Merging features of layer: {}".format(layer_name))

        self._prepare_features_for_dissolvment(layer)
        dissolved_layer = self._dissolve(layer)

        return dissolved_layer
示例#20
0
 def initGui(self):
     self._load_recently_used()
     self.action = QAction(QIcon(':/plugins/vectortilereader/icon.png'),
                           "Add Vector Tiles Layer",
                           self.iface.mainWindow())
     self.action.triggered.connect(self.run)
     self.iface.addPluginToMenu("&Vector Tiles Reader", self.action)
     self.iface.addPluginToVectorMenu("&Vector Tiles Reader", self.action)
     self.settingsaction = QAction(
         QIcon(':/plugins/vectortilereader/icon.png'), "Settings",
         self.iface.mainWindow())
     self.settingsaction.triggered.connect(self.edit_sources)
     self.iface.addPluginToMenu("&Vector Tiles Reader", self.settingsaction)
     self.init_vt_reader()
     self.add_menu()
     info("Vector Tile Reader Plugin loaded...")
示例#21
0
    def sell(self, stk, price, vol):
        price = round(float(price), 2)
        if vol * price > self.riskLimit:
            log_helper.error(
                '[error]sell[stock:{0},price:{1},vol:{2}]money out of risklimit'
                .format(stk, price, vol))
            return

        log_helper.info(
            '[info]sell[stock:{0},price:{1},vol:{2},money:{3}]'.format(
                stk, price, vol, vol * price))
        return self._sendOrder(str(stk),
                               float(price),
                               int(vol),
                               category=1,
                               priceType=0)
示例#22
0
 def connect_market_server(self):
     try_times = 0
     len_market_servers = len(market_server_addresses)
     while try_times<300:
         try_times += 1
         try:
             server_addr = market_server_addresses[try_times%len_market_servers]
             self.clientHq = TradeX.TdxHq_Connect(server_addr[0], server_addr[1])
             log_helper.info('connect hq success')
             break
         except TradeX.TdxHq_error, e:
             log_helper.error("connect hq fail TdxHq_error error: {0}".format(e.message))
             time.sleep(0.5)
         except Exception,e:
             log_helper.error("connect hq fail unkown error: {0}".format(e.message))
             time.sleep(0.5)
示例#23
0
 def _cancel_byID(self, exchangeID, orderID, result, errInf):
     if self.is_tradex:
         errInf, result = self.client.CancelOrder(int(exchangeID), orderID)
         log_helper.info(
             'cancel_withID[{0},order_id:{1}] result:{2} errInf:{3}'.format(
                 exchangeID, orderID, result.decode("gbk"), errInf))
     else:
         self.dll.CancelOrder(self.client, exchangeID, str(orderID), result,
                              errInf)
         try:
             log_helper.info(
                 'cancel_withID[{0},order_id:{1}] result:{2} errInf:{3}'.
                 format(exchangeID, orderID, result.decode("gbk"), errInf))
         except UnicodeDecodeError:
             log_helper.error(
                 'cancel_withID[{0},order_id:{1}] error:{2}'.format(
                     exchangeID, orderID, errInf.decode("gbk")))
    def _decode_tiles(self, tiles_with_encoded_data):
        """
         * Decodes the PBF data from all the specified tiles and reports the progress
         * If a tile is loaded from the cache, the decoded_data is already set and doesn't have to be encoded
        :param tiles_with_encoded_data:
        :return:
        """
        total_nr_tiles = len(tiles_with_encoded_data)
        self._update_progress(progress=0, max_progress=100, msg="Decoding {} tiles...".format(total_nr_tiles))

        nr_processors = 4
        try:
            nr_processors = mp.cpu_count()
        except NotImplementedError:
            info("CPU count cannot be retrieved. Falling back to default = 4")

        tiles_with_encoded_data = map(lambda t: (t[0], self._unzip(t[1])), tiles_with_encoded_data)

        pool = mp.Pool(nr_processors)
        tiles = []
        rs = pool.map_async(decode_tile, tiles_with_encoded_data, callback=tiles.extend)
        pool.close()
        current_progress = 0
        while not rs.ready() and not self.cancel_requested:
            if self.cancel_requested:
                pool.terminate()
                break
            else:
                QApplication.processEvents()
                remaining = rs._number_left
                index = total_nr_tiles - remaining
                progress = int(100.0 / total_nr_tiles * (index + 1))
                if progress != current_progress:
                    current_progress = progress
                    self._update_progress(progress=progress)

        tiles = filter(lambda ti: ti.decoded_data is not None, tiles)
        for t in tiles:
            if self.cancel_requested:
                break
            else:
                cache_file_name = self._get_tile_cache_name(t.zoom_level, t.column, t.row)
                if not os.path.isfile(cache_file_name):
                    FileHelper.cache_tile(t, cache_file_name)

        return tiles
    def _on_scale_or_extent_change_during_pause(self):
        assert self._current_reader
        self._scale_to_load = None
        self._extent_to_load = None

        info("got request in pause")
        has_scale_changed, new_target_scale, has_scale_increased = self._has_scale_changed(
        )
        if has_scale_changed:
            self._scale_to_load = None
        else:
            has_extent_changed, new_target_extent = self._has_extent_changed()
            if has_extent_changed:
                self._extent_to_load = new_target_extent

        if self._is_loading and (self._scale_to_load or self._extent_to_load):
            self._cancel_load()
 def _process_tiles(self, tiles, layer_filter):
     """
      * Creates GeoJSON for all the specified tiles and reports the progress
     :param tiles: 
     :return: 
     """
     total_nr_tiles = len(tiles)
     info("Processing {} tiles", total_nr_tiles)
     self._update_progress(progress=0, max_progress=100, msg="Processing features...")
     current_progress = -1
     for index, tile in enumerate(tiles):
         if self.cancel_requested:
             break
         self._create_geojson(tile, layer_filter)
         progress = int(100.0 / total_nr_tiles * (index + 1))
         if progress != current_progress:
             current_progress = progress
             self._update_progress(progress=progress)
    def reader_cancelled(self):
        info("cancelled")
        self._is_loading = False
        if self.progress_dialog:
            self.progress_dialog.hide()
        if self._auto_zoom:
            extent = self._extent_to_load
            reload_immediate = self._scale_to_load is not None or extent
            if reload_immediate:
                if self._scale_to_load:
                    self._loaded_scale = self._scale_to_load
                if extent:
                    self._loaded_extent = extent

                self._extent_to_load = None
                self._scale_to_load = None
                self._reload_tiles(overwrite_extent=extent)
            else:
                self._debouncer.start(start_immediate=True)
示例#28
0
    def _sendOrders(self, stks, prices, vols, categorys):
        '''
        
        stks,prices,vols 都是list格式的
        Category
            委托种类的数组,第i个元素表示第i个委托的种类,
            0买入 1卖出  2融资买入  3融券卖出   4买券还券   5卖券还款  6现券还券
        PriceType
            表示报价方式的数组,  第i个元素表示第i个委托的报价方式, 
            0上海限价委托 深圳限价委托 
            1(市价委托)深圳对方最优价格  
            2(市价委托)深圳本方最优价格  
            3(市价委托)深圳即时成交剩余撤销  
            4(市价委托)上海五档即成剩撤 深圳五档即成剩撤 
            5(市价委托)深圳全额成交或撤销 
            6(市价委托)上海五档即成转限价
        
        '''
        stkNo = len(stks)

        intArray = ctypes.c_int * stkNo
        strArray = ctypes.c_char_p * stkNo
        floatArray = ctypes.c_float * stkNo

        result = strArray(*(['\0' * 256] * stkNo))
        errInf = strArray(*(['\0' * 256] * stkNo))

        c_categorys = intArray(*categorys)
        c_priceTypes = intArray(*([0] * stkNo))

        c_gddms = strArray(*[(self.stockholder_code_sh if i[0] ==
                              '6' else self.stockholder_code_sz)
                             for i in stks])
        c_stks = strArray(*stks)
        c_prices = floatArray(*prices)
        c_vols = intArray(*vols)

        self.dll.SendOrders(self.client, c_categorys, c_priceTypes, c_gddms,
                            c_stks, c_prices, c_vols, stkNo, result, errInf)
        for i in range(stkNo):
            log_helper.info(result[i].decode("gbk"))
            log_helper.info(errInf[i].decode("gbk"))
 def handle_progress_update(self,
                            title=None,
                            progress=None,
                            max_progress=None,
                            msg=None,
                            show_progress=None):
     if show_progress:
         self.progress_dialog.open()
     elif show_progress is False:
         self.progress_dialog.hide()
         self.progress_dialog.set_message(None)
     if title:
         self.progress_dialog.setWindowTitle(title)
     if max_progress is not None:
         self.progress_dialog.set_maximum(max_progress)
     if msg:
         info(msg)
         self.progress_dialog.set_message(msg)
     if progress is not None:
         self.progress_dialog.set_progress(progress)
 def initGui(self):
     self.popupMenu = QMenu(self.iface.mainWindow())
     self.open_connections_action = self._create_action(
         "Add Vector Tiles Layer...", "server.svg",
         self._show_connections_dialog)
     self.reload_action = self._create_action(self._reload_button_text,
                                              "reload.svg",
                                              self._reload_tiles, False)
     self.export_action = self._create_action("Export selected layers",
                                              "save.svg",
                                              self._export_tiles)
     self.clear_cache_action = self._create_action("Clear cache",
                                                   "delete.svg",
                                                   FileHelper.clear_cache)
     self.about_action = self._create_action("About", "info.svg",
                                             self.show_about)
     self.iface.insertAddLayerAction(
         self.open_connections_action
     )  # Add action to the menu: Layer->Add Layer
     self.popupMenu.addAction(self.open_connections_action)
     self.popupMenu.addAction(self.reload_action)
     self.popupMenu.addAction(self.export_action)
     self.popupMenu.addAction(self.clear_cache_action)
     self.popupMenu.addAction(self.about_action)
     self.toolButton = QToolButton()
     self.toolButton.setMenu(self.popupMenu)
     self.toolButton.setDefaultAction(self.open_connections_action)
     self.toolButton.setPopupMode(QToolButton.MenuButtonPopup)
     self.toolButtonAction = self.iface.layerToolBar().addWidget(
         self.toolButton)
     self.iface.addPluginToVectorMenu("&Vector Tiles Reader",
                                      self.open_connections_action)
     self.iface.addPluginToVectorMenu("&Vector Tiles Reader",
                                      self.reload_action)
     self.iface.addPluginToVectorMenu("&Vector Tiles Reader",
                                      self.export_action)
     self.iface.addPluginToVectorMenu("&Vector Tiles Reader",
                                      self.clear_cache_action)
     self.iface.addPluginToVectorMenu("&Vector Tiles Reader",
                                      self.about_action)
     info("Vector Tile Reader Plugin loaded...")