NiceGUI 内置Material Design图标库

图:

代码:

python 复制代码
from nicegui import ui, events
from typing import List, Dict

# ========== 1. 扩展到200+ NiceGUI内置Material Design图标 ==========
MATERIAL_ICONS: List[Dict[str, str]] = [
    # 导航类(25个)
    {'name': 'menu', 'description': '菜单', 'category': '导航'},
    {'name': 'home', 'description': '首页', 'category': '导航'},
    {'name': 'search', 'description': '搜索', 'category': '导航'},
    {'name': 'arrow_back', 'description': '返回', 'category': '导航'},
    {'name': 'arrow_forward', 'description': '前进', 'category': '导航'},
    {'name': 'arrow_upward', 'description': '向上', 'category': '导航'},
    {'name': 'arrow_downward', 'description': '向下', 'category': '导航'},
    {'name': 'chevron_left', 'description': '左箭头', 'category': '导航'},
    {'name': 'chevron_right', 'description': '右箭头', 'category': '导航'},
    {'name': 'expand_more', 'description': '展开更多', 'category': '导航'},
    {'name': 'expand_less', 'description': '收起', 'category': '导航'},
    {'name': 'menu_open', 'description': '展开菜单', 'category': '导航'},
    {'name': 'menu_book', 'description': '菜单书', 'category': '导航'},
    {'name': 'close', 'description': '关闭', 'category': '导航'},
    {'name': 'backspace', 'description': '退格', 'category': '导航'},
    {'name': 'arrow_left', 'description': '左箭头', 'category': '导航'},
    {'name': 'arrow_right', 'description': '右箭头', 'category': '导航'},
    {'name': 'arrow_up', 'description': '上箭头', 'category': '导航'},
    {'name': 'arrow_down', 'description': '下箭头', 'category': '导航'},
    {'name': 'navigate_before', 'description': '导航前', 'category': '导航'},
    {'name': 'navigate_next', 'description': '导航后', 'category': '导航'},
    {'name': 'keyboard_arrow_up', 'description': '键盘上箭头', 'category': '导航'},
    {'name': 'keyboard_arrow_down', 'description': '键盘下箭头', 'category': '导航'},
    {'name': 'keyboard_arrow_left', 'description': '键盘左箭头', 'category': '导航'},
    {'name': 'keyboard_arrow_right', 'description': '键盘右箭头', 'category': '导航'},
    
    # 操作类(28个)
    {'name': 'add', 'description': '添加', 'category': '操作'},
    {'name': 'edit', 'description': '编辑', 'category': '操作'},
    {'name': 'delete', 'description': '删除', 'category': '操作'},
    {'name': 'save', 'description': '保存', 'category': '操作'},
    {'name': 'cancel', 'description': '取消', 'category': '操作'},
    {'name': 'check', 'description': '确认', 'category': '操作'},
    {'name': 'undo', 'description': '撤销', 'category': '操作'},
    {'name': 'redo', 'description': '重做', 'category': '操作'},
    {'name': 'copy', 'description': '复制', 'category': '操作'},
    {'name': 'cut', 'description': '剪切', 'category': '操作'},
    {'name': 'paste', 'description': '粘贴', 'category': '操作'},
    {'name': 'select_all', 'description': '全选', 'category': '操作'},
    {'name': 'refresh', 'description': '刷新', 'category': '操作'},
    {'name': 'download', 'description': '下载', 'category': '操作'},
    {'name': 'upload', 'description': '上传', 'category': '操作'},
    {'name': 'share', 'description': '分享', 'category': '操作'},
    {'name': 'send', 'description': '发送', 'category': '操作'},
    {'name': 'clear', 'description': '清除', 'category': '操作'},
    {'name': 'done', 'description': '完成', 'category': '操作'},
    {'name': 'remove', 'description': '移除', 'category': '操作'},
    {'name': 'create', 'description': '创建', 'category': '操作'},
    {'name': 'archive', 'description': '归档', 'category': '操作'},
    {'name': 'restore', 'description': '恢复', 'category': '操作'},
    {'name': 'backup', 'description': '备份', 'category': '操作'},
    {'name': 'import_export', 'description': '导入导出', 'category': '操作'},
    {'name': 'move_to_inbox', 'description': '移入收件箱', 'category': '操作'},
    {'name': 'unarchive', 'description': '取消归档', 'category': '操作'},
    {'name': 'publish', 'description': '发布', 'category': '操作'},
    
    # 功能类(26个)
    {'name': 'settings', 'description': '设置', 'category': '功能'},
    {'name': 'person', 'description': '个人', 'category': '功能'},
    {'name': 'email', 'description': '邮件', 'category': '功能'},
    {'name': 'phone', 'description': '电话', 'category': '功能'},
    {'name': 'lock', 'description': '锁定', 'category': '功能'},
    {'name': 'lock_open', 'description': '解锁', 'category': '功能'},
    {'name': 'visibility', 'description': '可见', 'category': '功能'},
    {'name': 'visibility_off', 'description': '隐藏', 'category': '功能'},
    {'name': 'favorite', 'description': '收藏', 'category': '功能'},
    {'name': 'star', 'description': '星星', 'category': '功能'},
    {'name': 'star_border', 'description': '空心星', 'category': '功能'},
    {'name': 'filter', 'description': '筛选', 'category': '功能'},
    {'name': 'sort', 'description': '排序', 'category': '功能'},
    {'name': 'more_vert', 'description': '更多(竖)', 'category': '功能'},
    {'name': 'more_horiz', 'description': '更多(横)', 'category': '功能'},
    {'name': 'account_circle', 'description': '账户头像', 'category': '功能'},
    {'name': 'badge', 'description': '徽章', 'category': '功能'},
    {'name': 'bookmark', 'description': '书签', 'category': '功能'},
    {'name': 'bookmark_border', 'description': '空心书签', 'category': '功能'},
    {'name': 'contact_page', 'description': '联系人页面', 'category': '功能'},
    {'name': 'dashboard', 'description': '仪表盘', 'category': '功能'},
    {'name': 'edit_note', 'description': '编辑笔记', 'category': '功能'},
    {'name': 'flag', 'description': '旗帜', 'category': '功能'},
    {'name': 'help_outline', 'description': '帮助轮廓', 'category': '功能'},
    {'name': 'history', 'description': '历史', 'category': '功能'},
    {'name': 'login', 'description': '登录', 'category': '功能'},
    
    # 提示类(12个)
    {'name': 'error', 'description': '错误', 'category': '提示'},
    {'name': 'warning', 'description': '警告', 'category': '提示'},
    {'name': 'info', 'description': '信息', 'category': '提示'},
    {'name': 'help', 'description': '帮助', 'category': '提示'},
    {'name': 'check_circle', 'description': '成功圈', 'category': '提示'},
    {'name': 'error_outline', 'description': '错误轮廓', 'category': '提示'},
    {'name': 'warning_amber', 'description': '警告琥珀色', 'category': '提示'},
    {'name': 'info_outline', 'description': '信息轮廓', 'category': '提示'},
    {'name': 'check_circle_outline', 'description': '成功圈轮廓', 'category': '提示'},
    {'name': 'report', 'description': '报告', 'category': '提示'},
    {'name': 'report_problem', 'description': '报告问题', 'category': '提示'},
    {'name': 'notifications', 'description': '通知', 'category': '提示'},
    
    # 文件类(18个)
    {'name': 'folder', 'description': '文件夹', 'category': '文件'},
    {'name': 'folder_open', 'description': '打开文件夹', 'category': '文件'},
    {'name': 'file', 'description': '文件', 'category': '文件'},
    {'name': 'file_copy', 'description': '复制文件', 'category': '文件'},
    {'name': 'image', 'description': '图片', 'category': '文件'},
    {'name': 'video_library', 'description': '视频库', 'category': '文件'},
    {'name': 'music_note', 'description': '音乐', 'category': '文件'},
    {'name': 'description', 'description': '文档', 'category': '文件'},
    {'name': 'file_download', 'description': '文件下载', 'category': '文件'},
    {'name': 'file_upload', 'description': '文件上传', 'category': '文件'},
    {'name': 'file_present', 'description': '文件展示', 'category': '文件'},
    {'name': 'folder_copy', 'description': '复制文件夹', 'category': '文件'},
    {'name': 'insert_drive_file', 'description': '插入文件', 'category': '文件'},
    {'name': 'pdf', 'description': 'PDF文件', 'category': '文件'},
    {'name': 'picture_as_pdf', 'description': 'PDF图片', 'category': '文件'},
    {'name': 'text_snippet', 'description': '文本片段', 'category': '文件'},
    {'name': 'upload_file', 'description': '上传文件', 'category': '文件'},
    {'name': 'cloud_upload', 'description': '云上传', 'category': '文件'},
    
    # 设备类(20个)
    {'name': 'laptop', 'description': '笔记本', 'category': '设备'},
    {'name': 'desktop_windows', 'description': '桌面', 'category': '设备'},
    {'name': 'phone_android', 'description': '安卓手机', 'category': '设备'},
    {'name': 'tablet_android', 'description': '平板', 'category': '设备'},
    {'name': 'print', 'description': '打印', 'category': '设备'},
    {'name': 'wifi', 'description': 'WiFi', 'category': '设备'},
    {'name': 'bluetooth', 'description': '蓝牙', 'category': '设备'},
    {'name': 'battery_full', 'description': '满电', 'category': '设备'},
    {'name': 'battery_half', 'description': '半电', 'category': '设备'},
    {'name': 'battery_empty', 'description': '无电', 'category': '设备'},
    {'name': 'battery_charging_full', 'description': '充电满', 'category': '设备'},
    {'name': 'camera', 'description': '相机', 'category': '设备'},
    {'name': 'headset', 'description': '耳机', 'category': '设备'},
    {'name': 'keyboard', 'description': '键盘', 'category': '设备'},
    {'name': 'mouse', 'description': '鼠标', 'category': '设备'},
    {'name': 'scanner', 'description': '扫描仪', 'category': '设备'},
    {'name': 'speaker', 'description': '扬声器', 'category': '设备'},
    {'name': 'tv', 'description': '电视', 'category': '设备'},
    {'name': 'usb', 'description': 'USB', 'category': '设备'},
    {'name': 'watch', 'description': '手表', 'category': '设备'},
    
    # 时间类(12个)
    {'name': 'calendar_today', 'description': '今日日历', 'category': '时间'},
    {'name': 'access_time', 'description': '时间', 'category': '时间'},
    {'name': 'alarm', 'description': '闹钟', 'category': '时间'},
    {'name': 'timer', 'description': '计时器', 'category': '时间'},
    {'name': 'stopwatch', 'description': '秒表', 'category': '时间'},
    {'name': 'calendar_month', 'description': '月历', 'category': '时间'},
    {'name': 'calendar_week', 'description': '周历', 'category': '时间'},
    {'name': 'clock', 'description': '时钟', 'category': '时间'},
    {'name': 'date_range', 'description': '日期范围', 'category': '时间'},
    {'name': 'schedule', 'description': '日程', 'category': '时间'},
    {'name': 'timer_10', 'description': '10分钟计时器', 'category': '时间'},
    {'name': 'timer_off', 'description': '关闭计时器', 'category': '时间'},
    
    # 购物类(15个)
    {'name': 'shopping_cart', 'description': '购物车', 'category': '购物'},
    {'name': 'payment', 'description': '支付', 'category': '购物'},
    {'name': 'tag', 'description': '标签', 'category': '购物'},
    {'name': 'barcode', 'description': '条形码', 'category': '购物'},
    {'name': 'qr_code', 'description': '二维码', 'category': '购物'},
    {'name': 'attach_money', 'description': '金额', 'category': '购物'},
    {'name': 'card_giftcard', 'description': '礼品卡', 'category': '购物'},
    {'name': 'credit_card', 'description': '信用卡', 'category': '购物'},
    {'name': 'money', 'description': '货币', 'category': '购物'},
    {'name': 'price_check', 'description': '价格检查', 'category': '购物'},
    {'name': 'receipt', 'description': '收据', 'category': '购物'},
    {'name': 'sell', 'description': '出售', 'category': '购物'},
    {'name': 'shopping_bag', 'description': '购物袋', 'category': '购物'},
    {'name': 'store', 'description': '商店', 'category': '购物'},
    {'name': 'local_atm', 'description': 'ATM机', 'category': '购物'},
    
    # 媒体类(18个)
    {'name': 'volume_up', 'description': '音量大', 'category': '媒体'},
    {'name': 'volume_down', 'description': '音量小', 'category': '媒体'},
    {'name': 'volume_mute', 'description': '静音', 'category': '媒体'},
    {'name': 'volume_off', 'description': '无音量', 'category': '媒体'},
    {'name': 'play_arrow', 'description': '播放', 'category': '媒体'},
    {'name': 'pause', 'description': '暂停', 'category': '媒体'},
    {'name': 'stop', 'description': '停止', 'category': '媒体'},
    {'name': 'skip_next', 'description': '下一曲', 'category': '媒体'},
    {'name': 'skip_previous', 'description': '上一曲', 'category': '媒体'},
    {'name': 'fast_forward', 'description': '快进', 'category': '媒体'},
    {'name': 'fast_rewind', 'description': '快退', 'category': '媒体'},
    {'name': 'repeat', 'description': '重复', 'category': '媒体'},
    {'name': 'shuffle', 'description': '随机播放', 'category': '媒体'},
    {'name': 'audiotrack', 'description': '音轨', 'category': '媒体'},
    {'name': 'mic', 'description': '麦克风', 'category': '媒体'},
    {'name': 'mic_off', 'description': '关闭麦克风', 'category': '媒体'},
    {'name': 'photo_camera', 'description': '拍照', 'category': '媒体'},
    {'name': 'video_call', 'description': '视频通话', 'category': '媒体'},
    
    # 工具类(20个)
    {'name': 'zoom_in', 'description': '放大', 'category': '工具'},
    {'name': 'zoom_out', 'description': '缩小', 'category': '工具'},
    {'name': 'fullscreen', 'description': '全屏', 'category': '工具'},
    {'name': 'fullscreen_exit', 'description': '退出全屏', 'category': '工具'},
    {'name': 'rotate_left', 'description': '左转', 'category': '工具'},
    {'name': 'rotate_right', 'description': '右转', 'category': '工具'},
    {'name': 'delete_sweep', 'description': '清除', 'category': '工具'},
    {'name': 'adjust', 'description': '调整', 'category': '工具'},
    {'name': 'build', 'description': '构建', 'category': '工具'},
    {'name': 'calculator', 'description': '计算器', 'category': '工具'},
    {'name': 'color_lens', 'description': '色镜', 'category': '工具'},
    {'name': 'compass_calibration', 'description': '罗盘校准', 'category': '工具'},
    {'name': 'crop', 'description': '裁剪', 'category': '工具'},
    {'name': 'draw', 'description': '绘制', 'category': '工具'},
    {'name': 'extension', 'description': '扩展', 'category': '工具'},
    {'name': 'flash_on', 'description': '闪光灯开', 'category': '工具'},
    {'name': 'flash_off', 'description': '闪光灯关', 'category': '工具'},
    {'name': 'highlight', 'description': '高亮', 'category': '工具'},
    {'name': 'level', 'description': '水平', 'category': '工具'},
    {'name': 'measure', 'description': '测量', 'category': '工具'}
]

# 默认图标颜色
DEFAULT_ICON_COLOR = '#4caf50'

# 获取所有分类(去重并排序)
def get_all_categories() -> List[str]:
    categories = sorted(list(set(icon['category'] for icon in MATERIAL_ICONS)))
    return ['全部图标'] + categories

# 复制图标名称函数(复制纯名称,如menu/home)
def copy_icon_name(name: str):
    ui.run_javascript(f'navigator.clipboard.writeText(`{name}`)')
    ui.notify(f'已复制图标名称: {name}', type='positive', timeout=2000)

def main():
    # 强制加载Material Icons字体(解决国内显示问题)
    ui.add_head_html('''
        <link href="https://fonts.googleapis.cn/css2?family=Material+Icons" rel="stylesheet">
    ''')

    # 页面基础配置
    ui.page_title('NiceGUI 3.4.0 Material Design图标库')
    ui.colors(primary='#4caf50', secondary='#03DAC6')  # Material Design主题色

    # 存储图标卡片引用和筛选状态
    icon_cards = {}  # key: 图标name, value: 卡片元素
    icon_elements = {}  # key: 图标name, value: 图标元素
    current_category = '全部图标'
    current_search_text = ''
    current_icon_color = DEFAULT_ICON_COLOR

    # ========== 顶部导航栏(分类筛选+搜索+颜色选择) ==========
    with ui.header(elevated=True).style('padding: 1rem 2rem; background-color: white; display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap'):
        # 标题
        ui.label('NiceGUI 内置Material Design图标库').style('font-size: 1.8rem; font-weight: bold; color: #4caf50; margin-right: 1rem')
        
        # 分类筛选下拉框
        category_select = ui.select(
            options=get_all_categories(),
            value='全部图标',
            on_change=lambda e: update_filter(category=e.value)
        ).props('rounded outlined').style('width: 180px; min-width: 150px')
        category_select.add_slot('prepend', '<i class="material-icons">folder_category</i>')
        
        # 搜索框(支持搜索名称/描述)
        search_input = ui.input(
            placeholder='搜索图标(名称/描述,如:菜单、arrow、home)',
            on_change=lambda e: update_filter(search_text=e.value)
        ).props('rounded outlined').style('width: 300px; min-width: 200px; flex-grow: 1; max-width: 500px')
        search_input.add_slot('prepend', '<i class="material-icons">search</i>')
        
        # 新增:图标颜色选择器
        color_picker = ui.color_input(
            label='图标颜色',
            value=DEFAULT_ICON_COLOR,
            on_change=lambda e: change_icon_color(e.value)
        ).props('rounded outlined hide-header').style('width: 180px; min-width: 150px')
        color_picker.add_slot('prepend', '<i class="material-icons">color_lens</i>')
        
        # 新增:重置颜色按钮
        ui.button(
            '重置颜色',
            on_click=lambda: reset_icon_color(),
            icon='refresh'
        ).props('outline rounded').style('height: 56px')
        
        # 统计显示
        count_label = ui.label(f'共 {len(MATERIAL_ICONS)} 个图标').style('margin-left: auto; color: #666; white-space: nowrap')

    # ========== 图标展示区域(响应式网格布局) ==========
    with ui.column().classes('w-full box-border'):
        # grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)) 表示:
        # - auto-fill: 自动填充列数
        # - minmax(120px, 1fr): 每列最小120px,最大自适应
        grid = ui.grid().style('''
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
            gap: 1.5rem;
            max-width: 1600px;
            margin: 0 auto;
            width: 100%;
        ''')
        
        # 生成所有图标卡片
        for icon in MATERIAL_ICONS:
            icon_name = icon['name']
            icon_desc = icon['description']
            icon_category = icon['category']
            
            with grid:
                # 优化卡片样式:移除固定宽度,使用百分比宽度,保证自适应
                card_style = (
                    'padding: 1rem 0.8rem; '
                    'text-align: center; '
                    'border-radius: 8px; '
                    'box-shadow: 0 2px 4px rgba(0,0,0,0.1); '
                    'width: 100%; '  # 改为100%宽度,适应网格列宽
                    'height: 200px; '
                    'display: flex; '
                    'flex-direction: column; '
                    'align-items: center; '
                    'justify-content: center; '
                    'transition: all 0.2s ease; '
                    'background-color: white; '
                    'overflow: hidden;'
                )
                card = ui.card().style(card_style)
                # 鼠标悬停效果(分开绑定,避免样式字符串解析错误)
                card.on('mouseenter', lambda e: e.sender.style('box-shadow: 0 4px 8px rgba(0,0,0,0.15); transform: translateY(-2px)'))
                card.on('mouseleave', lambda e: e.sender.style('box-shadow: 0 2px 4px rgba(0,0,0,0.1); transform: translateY(0)'))
                
                with card:
                    with ui.column().classes('gap-0 items-center w-full'):
                        # 适配尺寸的图标 - 保存图标元素引用
                        icon_elem = ui.icon(icon_name).style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {current_icon_color}')
                        # 紧凑的文字样式
                        name_style = (
                            'font-family: Consolas, monospace; '
                            'font-size: 12px; '
                            'font-weight: bold; '
                            'margin-bottom: 0.2rem; '
                            'color: #333; '
                            'word-break: break-all; '
                            'line-height: 1.2;'
                        )
                        ui.label(icon_name).style(name_style)
                        ui.label(icon_desc).style('font-size: 11px; color: #666; margin-bottom: 0.3rem')
                        ui.label(icon_category).props('color=secondary text-xs').style('margin-bottom: 0.3rem')
                        # 小巧的复制按钮
                        ui.button('复制', on_click=lambda n=icon_name: copy_icon_name(n)) \
                            .props('outline rounded').style('width: 80px; height: 30px; font-size: 11px')
                
                # 保存卡片和图标元素引用
                icon_cards[icon_name] = card
                icon_elements[icon_name] = icon_elem

    # ========== 颜色修改函数 ==========
    def change_icon_color(new_color: str):
        """修改所有显示图标的颜色"""
        nonlocal current_icon_color
        current_icon_color = new_color
        
        # 遍历所有图标元素更新颜色
        for icon_name, icon_elem in icon_elements.items():
            # 只更新显示中的图标
            if icon_cards[icon_name].style.get('display', 'flex') == 'flex':
                icon_elem.style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {new_color}')
        
        ui.notify(f'图标颜色已更新为: {new_color}', type='info', timeout=2000)

    def reset_icon_color():
        """重置图标颜色为默认值"""
        nonlocal current_icon_color
        current_icon_color = DEFAULT_ICON_COLOR
        
        # 更新颜色选择器值
        color_picker.value = DEFAULT_ICON_COLOR
        
        # 重置所有图标颜色
        for icon_name, icon_elem in icon_elements.items():
            if icon_cards[icon_name].style.get('display', 'flex') == 'flex':
                icon_elem.style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {DEFAULT_ICON_COLOR}')
        
        ui.notify('图标颜色已重置为默认值', type='success', timeout=2000)

    # ========== 组合筛选函数(分类+搜索) ==========
    def update_filter(category: str = None, search_text: str = None):
        nonlocal current_category, current_search_text
        
        # 更新筛选状态
        if category is not None:
            current_category = category
        if search_text is not None:
            current_search_text = search_text.lower().strip()
        
        match_count = 0
        # 遍历所有图标应用筛选
        for icon in MATERIAL_ICONS:
            icon_name = icon['name']
            card = icon_cards[icon_name]
            icon_elem = icon_elements[icon_name]
            
            # 条件1:分类匹配(全部图标则跳过分类筛选)
            category_match = (current_category == '全部图标') or (icon['category'] == current_category)
            # 条件2:搜索匹配(名称/描述包含关键词)
            search_match = True
            if current_search_text:
                search_match = (current_search_text in icon_name.lower()) or (current_search_text in icon['description'].lower())
            
            # 显示/隐藏卡片,并确保显示的图标使用当前颜色
            if category_match and search_match:
                card.style('display: flex')
                # 确保显示的图标使用当前选中的颜色
                icon_elem.style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {current_icon_color}')
                match_count += 1
            else:
                card.style('display: none')
        
        # 更新统计
        total_in_category = len(MATERIAL_ICONS) if current_category == '全部图标' else len([i for i in MATERIAL_ICONS if i['category'] == current_category])
        count_label.set_text(f'找到 {match_count} 个图标({current_category}共 {total_in_category} 个)')

    # ========== 页脚 ==========
    with ui.footer().style('padding: 0.8rem; text-align: center; background-color: #4caf50; color: white'):
        ui.label('NiceGUI 3.4.0 原生Material Design图标 | 200+常用图标 | 支持分类筛选+关键词搜索+颜色自定义 | 可直接复制图标名称使用')

# 兼容多进程的主程序保护语句
if __name__ in {"__main__", "__mp_main__"}:
    main()
    # 启动NiceGUI
    ui.run(
        title='NiceGUI Material图标库',
        port=8080,
        reload=True,
        show=True,
        uvicorn_logging_level='warning'
    )

NiceGUI Material Design 图标库

项目介绍

本项目是基于 NiceGUI 3.4.0 开发的 Material Design 图标库工具,整合了 200+ 常用内置图标,支持按分类筛选、关键词搜索、图标颜色自定义及图标名称一键复制功能。界面简洁直观、响应式布局适配多种屏幕尺寸,旨在为 NiceGUI 开发者提供便捷的图标选型与使用体验。

核心功能

  • 丰富的图标资源:包含 200+ 个 Material Design 内置图标,涵盖导航、操作、功能、提示、文件、设备等 10 大分类,满足日常开发需求。
  • 多维度筛选:支持按分类筛选(如"导航""操作""文件"等),同时支持关键词搜索(匹配图标名称或描述),快速定位目标图标。
  • 自定义图标颜色:内置颜色选择器,可自由切换图标显示颜色,支持十六进制颜色值输入,同时提供"重置颜色"功能快速恢复默认色。
  • 一键复制功能 :每个图标卡片配备"复制"按钮,点击即可复制图标名称(如 menuhome),直接用于 NiceGUI 项目开发。
  • 响应式布局:采用自适应网格布局,在电脑、平板等不同尺寸设备上均能良好展示。
  • 友好的视觉反馈:图标卡片hover效果、操作成功提示(复制、颜色修改)等,提升使用体验。

环境要求

  • Python 3.8+
  • NiceGUI 3.4.0+(推荐与项目依赖版本一致)

安装步骤

1. 克隆/下载项目

将项目代码下载到本地,或通过 git 克隆(若使用版本控制):

bash 复制代码
git clone <项目仓库地址>
cd nicegui-material-icon-library

2. 安装依赖

使用 pip 安装所需依赖(主要为 NiceGUI):

ini 复制代码
pip install nicegui==3.4.0

若需快速安装最新兼容版本,可简化为:

复制代码
pip install nicegui

运行方法

在项目根目录下,执行以下命令启动程序:

复制代码
python icon_library.py

程序启动后,会自动在默认浏览器中打开页面(默认地址:http://localhost:8080)。若浏览器未自动打开,可手动访问该地址。

使用指南

1. 浏览与筛选图标

  • 分类筛选:点击顶部导航栏的"分类筛选"下拉框,选择目标分类(如"导航""操作"),页面将仅显示该分类下的图标。选择"全部图标"可恢复显示所有图标。
  • 关键词搜索:在顶部搜索框中输入关键词(支持图标名称或描述,如"菜单""arrow""home"),页面将实时筛选出匹配的图标。清空搜索框可恢复显示所有图标。

2. 自定义图标颜色

  • 选择颜色:点击顶部导航栏的"图标颜色"选择器,通过颜色面板选择所需颜色,或直接输入十六进制颜色值(如 #ff0000、#2196f3),页面中所有显示的图标将实时更新为所选颜色。
  • 重置颜色:点击"重置颜色"按钮,可快速将所有图标恢复为默认颜色(#4caf50,Material Design 绿色)。

3. 复制图标名称

找到目标图标后,点击图标卡片下方的"复制"按钮,系统将自动复制该图标的名称(如 searchdelete),并显示"已复制图标名称"的成功提示。复制后可直接在 NiceGUI 项目中使用该名称创建图标,示例:

python 复制代码
from nicegui import ui

ui.icon('menu')  # 使用复制的图标名称
ui.run()

4. 查看图标信息

每个图标卡片包含以下信息:

  • 图标预览:直观展示图标样式
  • 图标名称:NiceGUI 中使用的图标标识(可复制)
  • 图标描述:中文说明,便于理解图标用途
  • 图标分类:该图标所属的分类

项目结构

bash 复制代码
nicegui-material-icon-library/
├── icon_library.py  # 主程序文件(核心代码)
└── README.md        # 项目说明文档

核心代码说明:

  • MATERIAL_ICONS:存储所有图标的信息(名称、描述、分类),可在此扩展或修改图标列表。
  • get_all_categories():获取所有图标分类(去重并排序),用于分类筛选下拉框。
  • copy_icon_name():实现图标名称复制功能,并显示提示信息。
  • change_icon_color():实现图标颜色更新功能。
  • reset_icon_color():实现图标颜色重置功能。
  • update_filter():组合分类筛选和关键词搜索功能,控制图标显示/隐藏。

扩展与定制

1. 增加更多图标

MATERIAL_ICONS 列表中添加新的图标字典,格式如下:

bash 复制代码
{
    'name': '图标名称',  # NiceGUI 支持的 Material Design 图标名称
    'description': '中文描述',  # 图标用途说明
    'category': '分类名称'  # 所属分类(如"导航""操作",可新增分类)
}

2. 修改默认配置

  • 默认颜色 :修改 DEFAULT_ICON_COLOR 变量的值,可更改图标默认颜色。
  • 页面主题 :修改 ui.colors() 中的 primary(主色)和 secondary(辅助色),可更改页面主题色。
  • 端口号 :修改 ui.run() 中的 port 参数,可更改程序运行端口(如 port=8081)。

3. 调整界面样式

可修改代码中相关的 style 属性,调整图标卡片大小、颜色、间距,导航栏样式,字体大小等,实现个性化界面定制。

常见问题

1. 图标无法正常显示?

  • 检查 NiceGUI 版本是否兼容(推荐 3.4.0+),若版本过低,可能导致部分图标无法显示。
  • 确保网络正常,程序需加载 Material Icons 字体(已配置国内镜像:fonts.googleapis.cn),网络异常可能导致图标加载失败。

2. 复制功能无效?

部分浏览器可能限制剪贴板访问权限,建议在浏览器地址栏旁确认权限(允许剪贴板访问),或尝试更换主流浏览器(如 Chrome、Edge、Firefox)。

3. 程序启动失败?

  • 检查端口是否被占用,若 8080 端口已被其他程序使用,可修改 ui.run() 中的 port 参数,使用其他空闲端口。
  • 检查 Python 版本是否符合要求(3.8+),过低版本可能导致语法错误。

致谢

  • 基于 NiceGUI 框架开发,感谢 NiceGUI 团队提供的简洁高效的开发工具。
  • 图标资源基于 Material Design Icons,感谢 Google 提供的开源图标库。

由于局域网不能复制改变下用(pyperclip):

python 复制代码
from nicegui import ui, events
from typing import List, Dict
import pyperclip  # 核心:引入pyperclip实现服务端复制
import sys

class MaterialIconLibrary:
    # ========== 类级常量定义 ==========
    MATERIAL_ICONS: List[Dict[str, str]] = [
        # 导航类(25个)
        {'name': 'menu', 'description': '菜单', 'category': '导航'},
        {'name': 'home', 'description': '首页', 'category': '导航'},
        {'name': 'search', 'description': '搜索', 'category': '导航'},
        {'name': 'arrow_back', 'description': '返回', 'category': '导航'},
        {'name': 'arrow_forward', 'description': '前进', 'category': '导航'},
        {'name': 'arrow_upward', 'description': '向上', 'category': '导航'},
        {'name': 'arrow_downward', 'description': '向下', 'category': '导航'},
        {'name': 'chevron_left', 'description': '左箭头', 'category': '导航'},
        {'name': 'chevron_right', 'description': '右箭头', 'category': '导航'},
        {'name': 'expand_more', 'description': '展开更多', 'category': '导航'},
        {'name': 'expand_less', 'description': '收起', 'category': '导航'},
        {'name': 'menu_open', 'description': '展开菜单', 'category': '导航'},
        {'name': 'menu_book', 'description': '菜单书', 'category': '导航'},
        {'name': 'close', 'description': '关闭', 'category': '导航'},
        {'name': 'backspace', 'description': '退格', 'category': '导航'},
        {'name': 'arrow_left', 'description': '左箭头', 'category': '导航'},
        {'name': 'arrow_right', 'description': '右箭头', 'category': '导航'},
        {'name': 'arrow_up', 'description': '上箭头', 'category': '导航'},
        {'name': 'arrow_down', 'description': '下箭头', 'category': '导航'},
        {'name': 'navigate_before', 'description': '导航前', 'category': '导航'},
        {'name': 'navigate_next', 'description': '导航后', 'category': '导航'},
        {'name': 'keyboard_arrow_up', 'description': '键盘上箭头', 'category': '导航'},
        {'name': 'keyboard_arrow_down', 'description': '键盘下箭头', 'category': '导航'},
        {'name': 'keyboard_arrow_left', 'description': '键盘左箭头', 'category': '导航'},
        {'name': 'keyboard_arrow_right', 'description': '键盘右箭头', 'category': '导航'},
        
        # 操作类(28个)
        {'name': 'add', 'description': '添加', 'category': '操作'},
        {'name': 'edit', 'description': '编辑', 'category': '操作'},
        {'name': 'delete', 'description': '删除', 'category': '操作'},
        {'name': 'save', 'description': '保存', 'category': '操作'},
        {'name': 'cancel', 'description': '取消', 'category': '操作'},
        {'name': 'check', 'description': '确认', 'category': '操作'},
        {'name': 'undo', 'description': '撤销', 'category': '操作'},
        {'name': 'redo', 'description': '重做', 'category': '操作'},
        {'name': 'copy', 'description': '复制', 'category': '操作'},
        {'name': 'cut', 'description': '剪切', 'category': '操作'},
        {'name': 'paste', 'description': '粘贴', 'category': '操作'},
        {'name': 'select_all', 'description': '全选', 'category': '操作'},
        {'name': 'refresh', 'description': '刷新', 'category': '操作'},
        {'name': 'download', 'description': '下载', 'category': '操作'},
        {'name': 'upload', 'description': '上传', 'category': '操作'},
        {'name': 'share', 'description': '分享', 'category': '操作'},
        {'name': 'send', 'description': '发送', 'category': '操作'},
        {'name': 'clear', 'description': '清除', 'category': '操作'},
        {'name': 'done', 'description': '完成', 'category': '操作'},
        {'name': 'remove', 'description': '移除', 'category': '操作'},
        {'name': 'create', 'description': '创建', 'category': '操作'},
        {'name': 'archive', 'description': '归档', 'category': '操作'},
        {'name': 'restore', 'description': '恢复', 'category': '操作'},
        {'name': 'backup', 'description': '备份', 'category': '操作'},
        {'name': 'import_export', 'description': '导入导出', 'category': '操作'},
        {'name': 'move_to_inbox', 'description': '移入收件箱', 'category': '操作'},
        {'name': 'unarchive', 'description': '取消归档', 'category': '操作'},
        {'name': 'publish', 'description': '发布', 'category': '操作'},
        
        # 功能类(26个)
        {'name': 'settings', 'description': '设置', 'category': '功能'},
        {'name': 'person', 'description': '个人', 'category': '功能'},
        {'name': 'email', 'description': '邮件', 'category': '功能'},
        {'name': 'phone', 'description': '电话', 'category': '功能'},
        {'name': 'lock', 'description': '锁定', 'category': '功能'},
        {'name': 'lock_open', 'description': '解锁', 'category': '功能'},
        {'name': 'visibility', 'description': '可见', 'category': '功能'},
        {'name': 'visibility_off', 'description': '隐藏', 'category': '功能'},
        {'name': 'favorite', 'description': '收藏', 'category': '功能'},
        {'name': 'star', 'description': '星星', 'category': '功能'},
        {'name': 'star_border', 'description': '空心星', 'category': '功能'},
        {'name': 'filter', 'description': '筛选', 'category': '功能'},
        {'name': 'sort', 'description': '排序', 'category': '功能'},
        {'name': 'more_vert', 'description': '更多(竖)', 'category': '功能'},
        {'name': 'more_horiz', 'description': '更多(横)', 'category': '功能'},
        {'name': 'account_circle', 'description': '账户头像', 'category': '功能'},
        {'name': 'badge', 'description': '徽章', 'category': '功能'},
        {'name': 'bookmark', 'description': '书签', 'category': '功能'},
        {'name': 'bookmark_border', 'description': '空心书签', 'category': '功能'},
        {'name': 'contact_page', 'description': '联系人页面', 'category': '功能'},
        {'name': 'dashboard', 'description': '仪表盘', 'category': '功能'},
        {'name': 'edit_note', 'description': '编辑笔记', 'category': '功能'},
        {'name': 'flag', 'description': '旗帜', 'category': '功能'},
        {'name': 'help_outline', 'description': '帮助轮廓', 'category': '功能'},
        {'name': 'history', 'description': '历史', 'category': '功能'},
        {'name': 'login', 'description': '登录', 'category': '功能'},
        
        # 提示类(12个)
        {'name': 'error', 'description': '错误', 'category': '提示'},
        {'name': 'warning', 'description': '警告', 'category': '提示'},
        {'name': 'info', 'description': '信息', 'category': '提示'},
        {'name': 'help', 'description': '帮助', 'category': '提示'},
        {'name': 'check_circle', 'description': '成功圈', 'category': '提示'},
        {'name': 'error_outline', 'description': '错误轮廓', 'category': '提示'},
        {'name': 'warning_amber', 'description': '警告琥珀色', 'category': '提示'},
        {'name': 'info_outline', 'description': '信息轮廓', 'category': '提示'},
        {'name': 'check_circle_outline', 'description': '成功圈轮廓', 'category': '提示'},
        {'name': 'report', 'description': '报告', 'category': '提示'},
        {'name': 'report_problem', 'description': '报告问题', 'category': '提示'},
        {'name': 'notifications', 'description': '通知', 'category': '提示'},
        
        # 文件类(18个)
        {'name': 'folder', 'description': '文件夹', 'category': '文件'},
        {'name': 'folder_open', 'description': '打开文件夹', 'category': '文件'},
        {'name': 'file', 'description': '文件', 'category': '文件'},
        {'name': 'file_copy', 'description': '复制文件', 'category': '文件'},
        {'name': 'image', 'description': '图片', 'category': '文件'},
        {'name': 'video_library', 'description': '视频库', 'category': '文件'},
        {'name': 'music_note', 'description': '音乐', 'category': '文件'},
        {'name': 'description', 'description': '文档', 'category': '文件'},
        {'name': 'file_download', 'description': '文件下载', 'category': '文件'},
        {'name': 'file_upload', 'description': '文件上传', 'category': '文件'},
        {'name': 'file_present', 'description': '文件展示', 'category': '文件'},
        {'name': 'folder_copy', 'description': '复制文件夹', 'category': '文件'},
        {'name': 'insert_drive_file', 'description': '插入文件', 'category': '文件'},
        {'name': 'pdf', 'description': 'PDF文件', 'category': '文件'},
        {'name': 'picture_as_pdf', 'description': 'PDF图片', 'category': '文件'},
        {'name': 'text_snippet', 'description': '文本片段', 'category': '文件'},
        {'name': 'upload_file', 'description': '上传文件', 'category': '文件'},
        {'name': 'cloud_upload', 'description': '云上传', 'category': '文件'},
        
        # 设备类(20个)
        {'name': 'laptop', 'description': '笔记本', 'category': '设备'},
        {'name': 'desktop_windows', 'description': '桌面', 'category': '设备'},
        {'name': 'phone_android', 'description': '安卓手机', 'category': '设备'},
        {'name': 'tablet_android', 'description': '平板', 'category': '设备'},
        {'name': 'print', 'description': '打印', 'category': '设备'},
        {'name': 'wifi', 'description': 'WiFi', 'category': '设备'},
        {'name': 'bluetooth', 'description': '蓝牙', 'category': '设备'},
        {'name': 'battery_full', 'description': '满电', 'category': '设备'},
        {'name': 'battery_half', 'description': '半电', 'category': '设备'},
        {'name': 'battery_empty', 'description': '无电', 'category': '设备'},
        {'name': 'battery_charging_full', 'description': '充电满', 'category': '设备'},
        {'name': 'camera', 'description': '相机', 'category': '设备'},
        {'name': 'headset', 'description': '耳机', 'category': '设备'},
        {'name': 'keyboard', 'description': '键盘', 'category': '设备'},
        {'name': 'mouse', 'description': '鼠标', 'category': '设备'},
        {'name': 'scanner', 'description': '扫描仪', 'category': '设备'},
        {'name': 'speaker', 'description': '扬声器', 'category': '设备'},
        {'name': 'tv', 'description': '电视', 'category': '设备'},
        {'name': 'usb', 'description': 'USB', 'category': '设备'},
        {'name': 'watch', 'description': '手表', 'category': '设备'},
        
        # 时间类(12个)
        {'name': 'calendar_today', 'description': '今日日历', 'category': '时间'},
        {'name': 'access_time', 'description': '时间', 'category': '时间'},
        {'name': 'alarm', 'description': '闹钟', 'category': '时间'},
        {'name': 'timer', 'description': '计时器', 'category': '时间'},
        {'name': 'stopwatch', 'description': '秒表', 'category': '时间'},
        {'name': 'calendar_month', 'description': '月历', 'category': '时间'},
        {'name': 'calendar_week', 'description': '周历', 'category': '时间'},
        {'name': 'clock', 'description': '时钟', 'category': '时间'},
        {'name': 'date_range', 'description': '日期范围', 'category': '时间'},
        {'name': 'schedule', 'description': '日程', 'category': '时间'},
        {'name': 'timer_10', 'description': '10分钟计时器', 'category': '时间'},
        {'name': 'timer_off', 'description': '关闭计时器', 'category': '时间'},
        
        # 购物类(15个)
        {'name': 'shopping_cart', 'description': '购物车', 'category': '购物'},
        {'name': 'payment', 'description': '支付', 'category': '购物'},
        {'name': 'tag', 'description': '标签', 'category': '购物'},
        {'name': 'barcode', 'description': '条形码', 'category': '购物'},
        {'name': 'qr_code', 'description': '二维码', 'category': '购物'},
        {'name': 'attach_money', 'description': '金额', 'category': '购物'},
        {'name': 'card_giftcard', 'description': '礼品卡', 'category': '购物'},
        {'name': 'credit_card', 'description': '信用卡', 'category': '购物'},
        {'name': 'money', 'description': '货币', 'category': '购物'},
        {'name': 'price_check', 'description': '价格检查', 'category': '购物'},
        {'name': 'receipt', 'description': '收据', 'category': '购物'},
        {'name': 'sell', 'description': '出售', 'category': '购物'},
        {'name': 'shopping_bag', 'description': '购物袋', 'category': '购物'},
        {'name': 'store', 'description': '商店', 'category': '购物'},
        {'name': 'local_atm', 'description': 'ATM机', 'category': '购物'},
        
        # 媒体类(18个)
        {'name': 'volume_up', 'description': '音量大', 'category': '媒体'},
        {'name': 'volume_down', 'description': '音量小', 'category': '媒体'},
        {'name': 'volume_mute', 'description': '静音', 'category': '媒体'},
        {'name': 'volume_off', 'description': '无音量', 'category': '媒体'},
        {'name': 'play_arrow', 'description': '播放', 'category': '媒体'},
        {'name': 'pause', 'description': '暂停', 'category': '媒体'},
        {'name': 'stop', 'description': '停止', 'category': '媒体'},
        {'name': 'skip_next', 'description': '下一曲', 'category': '媒体'},
        {'name': 'skip_previous', 'description': '上一曲', 'category': '媒体'},
        {'name': 'fast_forward', 'description': '快进', 'category': '媒体'},
        {'name': 'fast_rewind', 'description': '快退', 'category': '媒体'},
        {'name': 'repeat', 'description': '重复', 'category': '媒体'},
        {'name': 'shuffle', 'description': '随机播放', 'category': '媒体'},
        {'name': 'audiotrack', 'description': '音轨', 'category': '媒体'},
        {'name': 'mic', 'description': '麦克风', 'category': '媒体'},
        {'name': 'mic_off', 'description': '关闭麦克风', 'category': '媒体'},
        {'name': 'photo_camera', 'description': '拍照', 'category': '媒体'},
        {'name': 'video_call', 'description': '视频通话', 'category': '媒体'},
        
        # 工具类(20个)
        {'name': 'zoom_in', 'description': '放大', 'category': '工具'},
        {'name': 'zoom_out', 'description': '缩小', 'category': '工具'},
        {'name': 'fullscreen', 'description': '全屏', 'category': '工具'},
        {'name': 'fullscreen_exit', 'description': '退出全屏', 'category': '工具'},
        {'name': 'rotate_left', 'description': '左转', 'category': '工具'},
        {'name': 'rotate_right', 'description': '右转', 'category': '工具'},
        {'name': 'delete_sweep', 'description': '清除', 'category': '工具'},
        {'name': 'adjust', 'description': '调整', 'category': '工具'},
        {'name': 'build', 'description': '构建', 'category': '工具'},
        {'name': 'calculator', 'description': '计算器', 'category': '工具'},
        {'name': 'color_lens', 'description': '色镜', 'category': '工具'},
        {'name': 'compass_calibration', 'description': '罗盘校准', 'category': '工具'},
        {'name': 'crop', 'description': '裁剪', 'category': '工具'},
        {'name': 'draw', 'description': '绘制', 'category': '工具'},
        {'name': 'extension', 'description': '扩展', 'category': '工具'},
        {'name': 'flash_on', 'description': '闪光灯开', 'category': '工具'},
        {'name': 'flash_off', 'description': '闪光灯关', 'category': '工具'},
        {'name': 'highlight', 'description': '高亮', 'category': '工具'},
        {'name': 'level', 'description': '水平', 'category': '工具'},
        {'name': 'measure', 'description': '测量', 'category': '工具'}
    ]
    
    # 默认图标颜色
    DEFAULT_ICON_COLOR = '#4caf50'

    def __init__(self):
        """初始化图标库实例,设置状态变量和UI组件引用"""
        # 状态变量
        self.current_category = '全部图标'
        self.current_search_text = ''
        self.current_icon_color = self.DEFAULT_ICON_COLOR
        
        # UI组件引用
        self.icon_cards = {}  # key: 图标name, value: 卡片元素
        self.icon_elements = {}  # key: 图标name, value: 图标元素
        self.category_select = None  # 分类筛选下拉框
        self.color_picker = None  # 颜色选择器
        self.count_label = None  # 统计标签

    @staticmethod
    def get_all_categories() -> List[str]:
        """获取所有分类(去重并排序)- 静态方法"""
        categories = sorted(list(set(icon['category'] for icon in MaterialIconLibrary.MATERIAL_ICONS)))
        return ['全部图标'] + categories

    def copy_icon_name(self, name: str):
        """
        核心改造:使用pyperclip实现服务端复制,兼容局域网
        复制完整的使用代码(参考案例的做法),而不只是图标名称
        """
        # 生成完整的使用代码(包含当前选中的颜色)
        if self.current_icon_color and self.current_icon_color != self.DEFAULT_ICON_COLOR:
            # 如果修改了颜色,复制带颜色的代码
            code = f"ui.icon('{name}', color='{self.current_icon_color}')"
        else:
            # 默认颜色,复制基础代码
            code = f"ui.icon('{name}')"
        
        try:
            # 使用pyperclip复制到剪贴板(服务端操作,不受浏览器限制)
            pyperclip.copy(code)
            ui.notify(f'已复制图标代码: {code}', type='positive', timeout=3000)
        except Exception as e:
            # 复制失败的兼容处理
            error_msg = f'复制失败: {str(e)}'
            ui.notify(f'{error_msg}\n请手动复制: {code}', type='negative', timeout=5000)
            # 备选方案:回退到文本框选中方案
            self.fallback_copy(name, code)

    def fallback_copy(self, name: str, code: str):
        """复制失败时的备选方案:创建临时文本框让用户手动复制"""
        # 创建隐藏的文本框(只在复制失败时显示)
        copy_input = ui.input(
            value=code,
            label='请手动复制图标代码'
        ).style('''
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 9999;
            width: 300px;
            opacity: 0.98;
        ''').props('readonly')
        
        # 自动选中文本框内容
        ui.run_javascript(f'''
            const input = document.querySelector('input[aria-label="请手动复制图标代码"]');
            if (input) {{
                input.focus();
                input.select();
                input.setSelectionRange(0, input.value.length);
            }}
        ''')
        
        # 5秒后自动隐藏文本框
        ui.timer(5.0, copy_input.delete, once=True)

    def change_icon_color(self, new_color: str):
        """修改所有显示图标的颜色 - 实例方法"""
        self.current_icon_color = new_color
        
        # 遍历所有图标元素更新颜色
        for icon_name, icon_elem in self.icon_elements.items():
            # 只更新显示中的图标
            if self.icon_cards[icon_name].style.get('display', 'flex') == 'flex':
                icon_elem.style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {new_color}')
        
        ui.notify(f'图标颜色已更新为: {new_color}', type='info', timeout=2000)

    def reset_icon_color(self):
        """重置图标颜色为默认值 - 实例方法"""
        self.current_icon_color = self.DEFAULT_ICON_COLOR
        
        # 更新颜色选择器值
        self.color_picker.value = self.DEFAULT_ICON_COLOR
        
        # 重置所有图标颜色
        for icon_name, icon_elem in self.icon_elements.items():
            if self.icon_cards[icon_name].style.get('display', 'flex') == 'flex':
                icon_elem.style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {self.DEFAULT_ICON_COLOR}')
        
        ui.notify('图标颜色已重置为默认值', type='success', timeout=2000)

    def update_filter(self, category: str = None, search_text: str = None):
        """组合筛选函数(分类+搜索)- 实例方法"""
        # 更新筛选状态
        if category is not None:
            self.current_category = category
        if search_text is not None:
            self.current_search_text = search_text.lower().strip()
        
        match_count = 0
        # 遍历所有图标应用筛选
        for icon in self.MATERIAL_ICONS:
            icon_name = icon['name']
            card = self.icon_cards[icon_name]
            icon_elem = self.icon_elements[icon_name]
            
            # 条件1:分类匹配(全部图标则跳过分类筛选)
            category_match = (self.current_category == '全部图标') or (icon['category'] == self.current_category)
            # 条件2:搜索匹配(名称/描述包含关键词)
            search_match = True
            if self.current_search_text:
                search_match = (self.current_search_text in icon_name.lower()) or (self.current_search_text in icon['description'].lower())
            
            # 显示/隐藏卡片,并确保显示的图标使用当前颜色
            if category_match and search_match:
                card.style('display: flex')
                # 确保显示的图标使用当前选中的颜色
                icon_elem.style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {self.current_icon_color}')
                match_count += 1
            else:
                card.style('display: none')
        
        # 更新统计
        total_in_category = len(self.MATERIAL_ICONS) if self.current_category == '全部图标' else len([i for i in self.MATERIAL_ICONS if i['category'] == self.current_category])
        self.count_label.set_text(f'找到 {match_count} 个图标({self.current_category}共 {total_in_category} 个)')

    def setup_ui(self):
        """设置UI界面 - 核心实例方法"""
        # 强制加载Material Icons字体(解决国内显示问题)
        ui.add_head_html('''
            <link href="https://fonts.googleapis.cn/css2?family=Material+Icons" rel="stylesheet">
        ''')

        # 页面基础配置
        ui.page_title('NiceGUI 3.4.0 Material Design图标库')
        ui.colors(primary='#4caf50', secondary='#03DAC6')  # Material Design主题色

        # ========== 顶部导航栏(分类筛选+搜索+颜色选择) ==========
        with ui.header(elevated=True).style('padding: 1rem 2rem; background-color: white; display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap'):
            # 标题
            ui.label('NiceGUI 内置Material Design图标库').style('font-size: 1.8rem; font-weight: bold; color: #4caf50; margin-right: 1rem')
            
            # 分类筛选下拉框 - 保存引用
            self.category_select = ui.select(
                options=self.get_all_categories(),
                value='全部图标',
                on_change=lambda e: self.update_filter(category=e.value)
            ).props('rounded outlined').style('width: 180px; min-width: 150px')
            self.category_select.add_slot('prepend', '<i class="material-icons">folder_category</i>')
            
            # 搜索框(支持搜索名称/描述)
            search_input = ui.input(
                placeholder='搜索图标(名称/描述,如:菜单、arrow、home)',
                on_change=lambda e: self.update_filter(search_text=e.value)
            ).props('rounded outlined').style('width: 300px; min-width: 200px; flex-grow: 1; max-width: 500px')
            search_input.add_slot('prepend', '<i class="material-icons">search</i>')
            
            # 图标颜色选择器 - 保存引用
            self.color_picker = ui.color_input(
                label='图标颜色',
                value=self.DEFAULT_ICON_COLOR,
                on_change=lambda e: self.change_icon_color(e.value)
            ).props('rounded outlined hide-header').style('width: 180px; min-width: 150px')
            self.color_picker.add_slot('prepend', '<i class="material-icons">color_lens</i>')
            
            # 重置颜色按钮
            ui.button(
                '重置颜色',
                on_click=lambda: self.reset_icon_color(),
                icon='refresh'
            ).props('outline rounded').style('height: 56px')
            
            # 统计显示 - 保存引用
            self.count_label = ui.label(f'共 {len(self.MATERIAL_ICONS)} 个图标').style('margin-left: auto; color: #666; white-space: nowrap')

        # ========== 图标展示区域(响应式网格布局) ==========
        with ui.column().classes('w-full box-border'):
            # grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)) 表示:
            # - auto-fill: 自动填充列数
            # - minmax(120px, 1fr): 每列最小120px,最大自适应
            grid = ui.grid().style('''
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
                gap: 1.5rem;
                max-width: 1600px;
                margin: 0 auto;
                width: 100%;
            ''')
            
            # 生成所有图标卡片
            for icon in self.MATERIAL_ICONS:
                icon_name = icon['name']
                icon_desc = icon['description']
                icon_category = icon['category']
                
                with grid:
                    # 优化卡片样式:移除固定宽度,使用百分比宽度,保证自适应
                    card_style = (
                        'padding: 1rem 0.8rem; '
                        'text-align: center; '
                        'border-radius: 8px; '
                        'box-shadow: 0 2px 4px rgba(0,0,0,0.1); '
                        'width: 100%; '  # 改为100%宽度,适应网格列宽
                        'height: 200px; '
                        'display: flex; '
                        'flex-direction: column; '
                        'align-items: center; '
                        'justify-content: center; '
                        'transition: all 0.2s ease; '
                        'background-color: white; '
                        'overflow: hidden;'
                    )
                    card = ui.card().style(card_style)
                    # 鼠标悬停效果(分开绑定,避免样式字符串解析错误)
                    card.on('mouseenter', lambda e: e.sender.style('box-shadow: 0 4px 8px rgba(0,0,0,0.15); transform: translateY(-2px)'))
                    card.on('mouseleave', lambda e: e.sender.style('box-shadow: 0 2px 4px rgba(0,0,0,0.1); transform: translateY(0)'))
                    
                    with card:
                        with ui.column().classes('gap-0 items-center w-full'):
                            # 适配尺寸的图标 - 保存图标元素引用
                            icon_elem = ui.icon(icon_name).style(f'font-size: 2.5rem; margin-bottom: 0.5rem; color: {self.current_icon_color}')
                            # 紧凑的文字样式
                            name_style = (
                                'font-family: Consolas, monospace; '
                                'font-size: 12px; '
                                'font-weight: bold; '
                                'margin-bottom: 0.2rem; '
                                'color: #333; '
                                'word-break: break-all; '
                                'line-height: 1.2;'
                            )
                            ui.label(icon_name).style(name_style)
                            ui.label(icon_desc).style('font-size: 11px; color: #666; margin-bottom: 0.3rem')
                            ui.label(icon_category).props('color=secondary text-xs').style('margin-bottom: 0.3rem')
                            # 小巧的复制按钮(参考案例,改用content_copy图标更直观)
                            ui.button(
                                icon='content_copy',  # 新增:添加复制图标,参考案例
                                on_click=lambda n=icon_name: self.copy_icon_name(n)
                            ).props('outline rounded').style('width: 80px; height: 30px; font-size: 11px').tooltip('复制')
                    
                    # 保存卡片和图标元素引用
                    self.icon_cards[icon_name] = card
                    self.icon_elements[icon_name] = icon_elem

        # ========== 页脚 ==========
        with ui.footer().style('padding: 0.8rem; text-align: center; background-color: #4caf50; color: white'):
            ui.label('NiceGUI 3.4.0 原生Material Design图标 | 200+常用图标 | 支持分类筛选+关键词搜索+颜色自定义 | 局域网环境下已支持一键复制')

# ========== 修复核心:将页面注册改为函数式,避免self参数冲突 ==========
# 创建全局实例(保证单例)
icon_library_instance = MaterialIconLibrary()

# 页面路由注册为普通函数,内部调用实例的setup_ui方法
@ui.page('/icon')
def icon_page():
    """图标库页面入口 - 普通函数,避免self参数问题"""
    icon_library_instance.setup_ui()

# ========== 程序调用示例 ==========
if __name__ in {"__main__", "__mp_main__"}:
    # 安装pyperclip提示(首次运行时)
    try:
        import pyperclip
    except ImportError:
        print("正在安装pyperclip库...")
        import subprocess
        subprocess.check_call([sys.executable, "-m", "pip", "install", "pyperclip"])
        import pyperclip
    
    # 启动NiceGUI(关键:添加host=0.0.0.0允许局域网访问)
    ui.run(
        title='NiceGUI Material图标库',
        reload=True,
        show=True,
        uvicorn_logging_level='warning',
        host='0.0.0.0'  # 必须:允许局域网其他设备访问
    )
相关推荐
Cache技术分享2 小时前
276. Java Stream API - 使用 flatMap 和 mapMulti 清理数据并转换类型
前端·后端
inferno2 小时前
CSS 基础(第一部分)
前端·css
m0_611349312 小时前
什么是副作用(Side Effects)
开发语言·前端·javascript
狗头大军之江苏分军2 小时前
她在结婚那天离开了:我们该重新谈谈“结婚这件事”
前端·后端
消失的旧时光-19432 小时前
从命令式跳转到声明式路由:前端、Android、Flutter 的一次统一演进
android·前端·flutter·状态模式
icestone_kai2 小时前
ngix开启跨域
前端
咸虾米_2 小时前
uniapp使用history路由模式打包上线到前端网页托管的注意事项
前端·uni-app·vue3·unicloud·前端网页托管
前端无涯2 小时前
React Router(web) 全解析:知识点、工作注意点及面试重点
前端·react.js·前端框架
EQ_雪梨蛋花汤2 小时前
【NDK / JNI】Sceneform-EQR 集成 Filament JNI 源码:关键点与逐步操作记录
前端