统信小程序(十一)快捷地址栏

复制代码
'''
制作一个linux中运行的一个名叫快捷地址访问的python程序,实现点击按钮就能直达地址。用于改善统信的任务栏体验。使用tktinter,软件界面尺寸300*30不带标题栏以便尽量压缩高度以便放置在任务栏上,底色淡黄色,一键处理的按键是糖果绿色,字体:fangsong ti,字号12号。 程序开始运行时,自动置顶,优先级高于任务栏。十个空白按钮,用来打开文件夹地址,默认地址分别是 /home/huanghe/, smb://192.168.1.207/sbg, smb://192.168.1.207/, 可以修改。一个关闭,一个设置按键,点击设置按键后可以重新定义三个按钮的打开地址并保存。
'''
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import subprocess
import json
import tkinter as tk
from tkinter import messagebox
from pathlib import Path
import time


class QuickAccess:
    def __init__(self, root):
        self.root = root
        self.root.title("快捷访问")

        # 设置窗口初始大小
        self.button_width = 3  # 每个按钮的基础宽度
        self.control_button_width = 10
        self.min_buttons = 3  # 最少按钮数
        self.window_height = 30

        # 配置文件路径
        self.config_file = os.path.expanduser("~/.quick_access_config.json")

        # 默认地址
        self.default_paths = [
            "/home/huanghe/",
            "smb://192.168.1.207/sbg",
            "smb://192.168.1.207/"
        ]

        # 加载配置(先创建button_names和paths属性)
        self.load_config()

        # 现在再计算窗口大小
        self.update_window_size()

        self.root.overrideredirect(True)  # 去掉标题栏
        self.root.attributes('-type', 'normal')  # 确保显示任务栏标签
        self.root.configure(bg='#FFFACD')  # 淡黄色背景

        # 设置窗口置顶
        self.root.attributes('-topmost', True)

        # 创建UI
        self.create_ui()

        # 绑定快捷键
        self.root.bind('<Control-q>', self.quit_app)
        self.root.bind('<Control-Q>', self.quit_app)

        # 允许自由调整大小
        self.root.resizable(True, False)  # 只允许水平调整

        # 设置最小窗口尺寸
        self.root.minsize(550, 30)

        # 延迟一点点确保窗口已创建,然后定位到屏幕下沿居中偏右
        self.root.after(100, self.position_window)

        # 确保窗口在任务栏上方
        self.root.lift()

    def update_window_size(self):
        """根据按钮数量更新窗口宽度(修复强制预留宽度问题)"""
        button_count = len(self.button_names)
        self.window_width = max(350, button_count * self.button_width + 80)  # 减少预留宽度
        self.root.geometry(f"{self.window_width}x{self.window_height}")

    def position_window(self):
        """启动时将窗口定位到屏幕下沿居中偏右"""
        # 获取屏幕宽度和高度
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()

        # 获取当前窗口大小
        current_width = self.root.winfo_width()
        current_height = self.root.winfo_height()

        # 计算任务栏高度(假设为40像素,可根据需要调整)
        taskbar_height = 40

        # 计算窗口位置:居中偏右(右侧留出一些空间)
        x = screen_width - current_width - 350  # 距离右边10像素
        y = screen_height - current_height - 1  # 任务栏上方5像素。去掉: - taskbar_height

        # 设置窗口位置
        self.root.geometry(f"+{x}+{y}")

    def create_ui(self):
        # 清除现有组件
        for widget in self.root.winfo_children():
            widget.destroy()

        # 主框架
        main_frame = tk.Frame(self.root, bg='#FFFACD')
        main_frame.pack(fill=tk.BOTH, expand=True, padx=1, pady=1)

        # 拆分框架:快捷按钮(拉伸) + 控制按钮(不拉伸)
        btn_frame = tk.Frame(main_frame, bg='#FFFACD')
        btn_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        ctrl_frame = tk.Frame(main_frame, bg='#FFFACD')
        ctrl_frame.pack(side=tk.LEFT, fill=tk.BOTH)

        # 按钮样式配置
        button_style = {
            'font': ('fangsong ti', 12),
            'bg': '#F5FFF5',  # 糖果绿色
            'fg': 'black',
            'relief': tk.RAISED,
            'bd': 1,
            'width': 1  # 限制宽度
        }

        # 创建所有快捷按钮(放在拉伸框架)
        self.buttons = []
        for i in range(len(self.button_names)):
            btn = tk.Button(
                btn_frame,
                text=self.button_names[i],
                command=lambda x=i: self.open_path(x),
                **button_style
            )
            btn.pack(side=tk.LEFT, padx=1, fill=tk.BOTH, expand=True)
            self.buttons.append(btn)

        # 设置按钮(放在不拉伸框架)
        settings_btn = tk.Button(
            ctrl_frame,
            text="设置",
            font=('fangsong ti', 11),
            bg='#FFB6C1',
            fg='black',
            relief=tk.RAISED,
            bd=1,
            width=3,
            padx=0,
            command=self.show_settings
        )
        settings_btn.pack(side=tk.LEFT, padx=0, fill=tk.BOTH)

        # 开机启动按钮(放在不拉伸框架)
        self.startup_btn = tk.Button(
            ctrl_frame,
            text="开",
            font=('fangsong ti', 11),
            bg='#98FB98',
            fg='black',
            relief=tk.RAISED,
            bd=1,
            width=2,
            padx=0,
            command=self.toggle_startup
        )
        self.startup_btn.pack(side=tk.LEFT, padx=0, fill=tk.BOTH)

        # 关闭按钮(完全自定义宽度,无拉伸)
        close_btn = tk.Button(
            ctrl_frame,
            text="✕",
            font=('fangsong ti', 11),
            bg='#F5fff4',
            fg='black',
            relief=tk.RAISED,
            bd=1,
            width=1,  # 独立控制宽度,不会被覆盖
            padx=2,
            pady=0,
            command=self.quit_app
        )
        close_btn.pack(side=tk.LEFT, padx=0, fill=tk.BOTH)

        # 更新开机启动按钮状态
        self.update_startup_button()

    def load_config(self):
        """加载配置文件"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    self.button_names = config.get('names', ["键1", "键2", "键3"])
                    self.paths = config.get('paths', self.default_paths.copy())
            else:
                self.button_names = ["键1", "键2", "键3"]
                self.paths = self.default_paths.copy()
        except Exception as e:
            print(f"加载配置失败: {e}")
            self.button_names = ["键1", "键2", "键3"]
            self.paths = self.default_paths.copy()

    def save_config(self):
        """保存配置"""
        try:
            config = {
                'paths': self.paths,
                'names': self.button_names
            }
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
        except Exception as e:
            messagebox.showerror("错误", f"保存配置失败: {e}")

    def open_path(self, index):
        """打开指定路径(最大化窗口)"""
        if index >= len(self.paths):
            return

        path = self.paths[index]

        try:
            # 方法1: 使用dbus发送最大化命令(统信专用)
            if path.startswith("smb://"):
                # 对于SMB路径,直接打开
                subprocess.Popen(["dde-file-manager", path])
            else:
                # 对于本地路径,先检查/创建目录
                if not os.path.exists(path):
                    try:
                        os.makedirs(path, exist_ok=True)
                    except:
                        pass

                # 使用dde-file-manager打开
                subprocess.Popen(["dde-file-manager", path])

            # 等待一下让文件管理器启动
            time.sleep(0.5)

            # 方法2: 使用wmctrl最大化窗口(如果安装了wmctrl)
            try:
                # 获取最近打开的文件管理器窗口并最大化
                result = subprocess.run(["wmctrl", "-l"], capture_output=True, text=True)
                if "dde-file-manager" in result.stdout.lower() or "文件管理器" in result.stdout:
                    # 找到最新的文件管理器窗口
                    lines = result.stdout.strip().split('\n')
                    for line in reversed(lines):
                        if "dde-file-manager" in line.lower() or "文件管理器" in line:
                            window_id = line.split()[0]
                            subprocess.run(["wmctrl", "-i", "-r", window_id, "-b", "add,maximized_vert,maximized_horz"])
                            break
            except:
                pass  # 如果没有wmctrl,忽略最大化操作

        except Exception as e:
            # 如果都失败了,尝试最简单的打开方式
            try:
                if path.startswith("smb://"):
                    subprocess.Popen(["dde-file-manager", path])
                else:
                    subprocess.Popen(["xdg-open", path])  # 使用xdg-open作为备选
            except:
                messagebox.showerror("错误", f"打开失败: {e}")

    def show_settings(self):
        """显示设置对话框"""
        settings_window = tk.Toplevel(self.root)
        settings_window.title("设置")
        settings_window.geometry("1000x500")
        settings_window.configure(bg='#FFFACD')
        settings_window.transient(self.root)
        settings_window.grab_set()

        # 设置窗口位置居中
        settings_window.update_idletasks()
        x = (settings_window.winfo_screenwidth() // 2) - (700 // 2)
        y = (settings_window.winfo_screenheight() // 2) - (500 // 2)
        settings_window.geometry(f"+{x}+{y}")

        # 标题
        title_label = tk.Label(
            settings_window,
            text="快捷访问设置",
            font=('fangsong ti', 14, 'bold'),
            bg='#FFFACD'
        )
        title_label.pack(pady=10)

        # 创建可滚动的设置区域
        canvas = tk.Canvas(settings_window, bg='#FFFACD', highlightthickness=0)
        scrollbar = tk.Scrollbar(settings_window, orient="vertical", command=canvas.yview)
        scrollable_frame = tk.Frame(canvas, bg='#FFFACD')

        scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )

        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)

        # 存储输入框的列表
        entries = []

        def add_button_row():
            """添加新的按钮行"""
            i = len(entries)

            # 创建一个框架来容纳整行
            row_frame = tk.Frame(scrollable_frame, bg='#FFFACD')
            row_frame.pack(fill=tk.X, padx=5, pady=1)

            # 按钮编号
            num_label = tk.Label(
                row_frame,
                text=f"{i + 1}.",
                font=('fangsong ti', 12, 'bold'),
                bg='#FFFACD',
                width=1
            )
            num_label.pack(side=tk.LEFT, padx=1)

            # 名称输入框
            name_entry = tk.Entry(row_frame, font=('fangsong ti', 12), width=8)
            name_entry.pack(side=tk.LEFT, padx=1)

            if i < len(self.button_names):
                name_entry.insert(0, self.button_names[i])
            else:
                name_entry.insert(0, f"键{i + 1}")

            # 路径输入框
            path_entry = tk.Entry(row_frame, font=('fangsong ti', 12), width=50)
            path_entry.pack(side=tk.LEFT, padx=1, fill=tk.X, expand=True)

            if i < len(self.paths):
                path_entry.insert(0, self.paths[i])

            # 删除按钮(只有超过3个的按钮才能删除)
            if i >= 3:
                delete_btn = tk.Button(
                    row_frame,
                    text="删除",
                    font=('fangsong ti', 10),
                    bg='#FF6B6B',
                    fg='white',
                    width=4,
                    command=lambda frame=row_frame, idx=i: delete_row(frame, idx)
                )
                delete_btn.pack(side=tk.RIGHT, padx=1)

            entries.append((name_entry, path_entry, row_frame))

        def delete_row(frame, index):
            """删除指定行"""
            if len(entries) <= 3:
                messagebox.showwarning("警告", "至少保留3个按钮")
                return

            # 从列表中移除
            for i, (_, _, f) in enumerate(entries):
                if f == frame:
                    entries.pop(i)
                    break

            # 销毁框架
            frame.destroy()

            # 重新编号所有行的标签
            for i, (name_entry, path_entry, row_frame) in enumerate(entries):
                # 更新行的编号
                for child in row_frame.winfo_children():
                    if isinstance(child, tk.Label) and child.cget('text').endswith('.'):
                        child.config(text=f"{i + 1}.")
                        break

        # 创建现有的所有按钮行
        max_count = max(len(self.button_names), len(self.paths), 3)
        for i in range(max_count):
            add_button_row()

        canvas.pack(side="left", fill="both", expand=True, padx=(5, 0))
        scrollbar.pack(side="right", fill="y")

        # 按钮框架
        button_frame = tk.Frame(settings_window, bg='#FFFACD')
        button_frame.pack(fill=tk.X, pady=10)

        # 添加按钮
        add_btn = tk.Button(
            button_frame,
            text="添加",
            font=('fangsong ti', 12),
            bg='#98FB98',
            command=lambda: [add_button_row(), settings_window.update_idletasks()]
        )
        add_btn.pack(side=tk.LEFT, padx=5)

        # 保存按钮
        def save_settings():
            new_names = []
            new_paths = []

            for name_entry, path_entry, _ in entries:
                name = name_entry.get().strip()
                path = path_entry.get().strip()

                if name:
                    new_names.append(name[:5])  # 限制长度为5
                else:
                    new_names.append(f"键{len(new_names) + 1}")

                new_paths.append(path if path else "")

            self.button_names = new_names
            self.paths = new_paths

            self.save_config()

            # 更新窗口大小
            self.update_window_size()

            # 重建UI
            self.create_ui()

            settings_window.destroy()
            messagebox.showinfo("成功", f"已保存 {len(new_names)} 个按钮")

        save_btn = tk.Button(
            button_frame,
            text="保存",
            font=('fangsong ti', 12),
            bg='#98FB98',
            command=save_settings
        )
        save_btn.pack(side=tk.LEFT, padx=8)

        # 取消按钮
        cancel_btn = tk.Button(
            button_frame,
            text="取消",
            font=('fangsong ti', 12),
            bg='#FFB6C1',
            command=settings_window.destroy
        )
        cancel_btn.pack(side=tk.LEFT, padx=8)

    def toggle_startup(self):
        """切换开机启动状态"""
        autostart_dir = os.path.expanduser("~/.config/autostart")
        desktop_file = os.path.join(autostart_dir, "quick-access.desktop")

        try:
            if os.path.exists(desktop_file):
                os.remove(desktop_file)
            else:
                os.makedirs(autostart_dir, exist_ok=True)
                script_path = os.path.abspath(__file__)

                desktop_content = f"""[Desktop Entry]
Type=Application
Name=快捷访问
Exec=python3 {script_path}
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Icon=system-file-manager
Comment=快捷访问文件夹工具
"""
                with open(desktop_file, 'w', encoding='utf-8') as f:
                    f.write(desktop_content)


            self.update_startup_button()

        except Exception as e:
            messagebox.showerror("错误", f"设置开机启动失败: {e}")

    def update_startup_button(self):
        """更新开机启动按钮状态"""
        autostart_file = os.path.expanduser("~/.config/autostart/quick-access.desktop")
        if os.path.exists(autostart_file):
            self.startup_btn.config(text="关", bg='#FF6B6B')
        else:
            self.startup_btn.config(text="开", bg='#98FB98')

    def quit_app(self, event=None):
        """退出程序"""
        self.root.quit()
        self.root.destroy()


def main():
    root = tk.Tk()
    app = QuickAccess(root)

    # 添加窗口拖动功能(点击背景拖动)
    def start_move(event):
        app.x = event.x
        app.y = event.y

    def stop_move(event):
        app.x = None
        app.y = None

    def do_move(event):
        if hasattr(app, 'x') and hasattr(app, 'y'):
            deltax = event.x - app.x
            deltay = event.y - app.y
            x = root.winfo_x() + deltax
            y = root.winfo_y() + deltay
            root.geometry(f"+{x}+{y}")

    # 绑定拖动事件到整个窗口背景
    root.bind('<Button-1>', start_move)
    root.bind('<ButtonRelease-1>', stop_move)
    root.bind('<B1-Motion>', do_move)

    # 防止按钮点击触发拖动
    def on_button_press(event):
        return

    for widget in root.winfo_children():
        widget.bind('<Button-1>', on_button_press, '+')

    root.mainloop()


if __name__ == "__main__":
    main()
相关推荐
weixin_421922692 小时前
机器学习模型部署:将模型转化为Web API
jvm·数据库·python
twc8292 小时前
Query 改写 大模型测试的数据倍增器
开发语言·人工智能·python·rag·大模型测试
Fortune792 小时前
Python迭代器(Iterator)揭秘:for循环背后的故事
jvm·数据库·python
cm6543202 小时前
Python字典与集合:高效数据管理的艺术
jvm·数据库·python
2401_846341652 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
CQU_JIAKE2 小时前
3.23[Q]s
开发语言·windows·python
黄昏晓x2 小时前
Linux----网络
linux·网络·arm开发
小比特_蓝光2 小时前
Linux开发工具
linux·运维·服务器
大熊背2 小时前
ISP离线模式应用(二)-如何利用 ISP 离线模式 加速 3DNR 收敛
linux·算法·rtos·isp pipeline·3dnr