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 大分类,满足日常开发需求。
  • 多维度筛选:支持按分类筛选(如"导航""操作""文件"等),同时支持关键词搜索(匹配图标名称或描述),快速定位目标图标。
  • 自定义图标颜色:内置颜色选择器,可自由切换图标显示颜色,支持十六进制颜色值输入,同时提供"重置颜色"功能快速恢复默认色。
  • 一键复制功能 :每个图标卡片配备"复制"按钮,点击即可复制图标名称(如 menu、home),直接用于 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. 复制图标名称

找到目标图标后,点击图标卡片下方的"复制"按钮,系统将自动复制该图标的名称(如 search、delete),并显示"已复制图标名称"的成功提示。复制后可直接在 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'  # 必须:允许局域网其他设备访问
    )
相关推荐
卸任3 小时前
Vibe coding工具使用心得
前端·ai编程
brien94 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance4 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M4 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写4 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李4 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易4 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马5 小时前
前沿视界
前端
kyriewen5 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易5 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员