Ubuntu环境下DearpyGUI2.0.0/Python3.12拖拽功能故障排查
Ubuntu下DearpyGUI 2.0.0 + Python3.12拖拽功能失效问题解决
问题概述
在Ubuntu系统中使用DearpyGUI 2.0.0与Python3.12实现拖拽功能时,已尝试关联父组件及子组件,但功能无法正常工作。拖拽图片或文件时,d.pending_events()输出始终为0。
运行输出
(env_312) (base) xam
a@xam-58:~/Desktop/Python/310py$ python test1.py
✅ Found
Parent window: 0x800df0
🔎 Dumping window tree:
↳ 0x800df0 name: Drag-and-Drop-Example
↳ 0x800df1 name: None
↳ 0x2a00009 name: Drag-and-Drop-Example
Child: 0x2a00009, name: Drag-and-Drop-Example
原测试代码
import dearpygui.dearpygui as dpg import threading import time from Xlib import X, display, Xatom #PARENT def _func_find_window_by_name(name, d): root = d.screen().root def search(w): for child in w.query_tree().children: try: win_name = child.get_wm_name() if win_name and name in win_name: return child result = search(child) if result: return result except: pass return None return search(root) #CHILDREN def find_real_content_window(parent): for child in parent.query_tree().children: name = child.get_wm_name() if name: print(f"Child: {hex(child.id)}, name: {name}") if name and "Drag-and-Drop-Example" in name: return child return parent # fallback def _func_setup_xdnd(win, d): XdndAware = d.intern_atom("XdndAware") win.change_property(XdndAware, Xatom.ATOM, 32, [5]) d.flush() # Verify prop = win.get_full_property(XdndAware, Xatom.ATOM) if prop: print(f"✅ XdndAware property set correctly: {prop.value}") print(f"✅ Using child content window: {hex(win.id)}") else: print("❌ Failed to set XdndAware property.") def _func_xdnd_loop(win, d): XdndEnter = d.intern_atom("XdndEnter") XdndPosition = d.intern_atom("XdndPosition") XdndDrop = d.intern_atom("XdndDrop") XdndSelection = d.intern_atom("XdndSelection") UTF8_STRING = d.intern_atom("UTF8_STRING") print(f"👂 Listening for XDND events on: {hex(win.id)}") while True: print(d.pending_events()) if d.pending_events(): print("🔄 Processing pending events...") # # e = d.next_event() # # if e.type == X.ClientMessage: # # if e.client_type == XdndEnter: # # print("📥 Drag entered") # # elif e.client_type == XdndPosition: # # print("📍 Drag position updated") # # elif e.client_type == XdndDrop: # # print("📦 Drop detected!") # # # Ask for the selection contents # # win.convert_selection(XdndSelection, UTF8_STRING, X.CurrentTime) # # elif e.type == X.SelectionNotify: # # prop = win.get_full_property(e.property, 0) # # if prop: # # uris = prop.value.decode("utf-8").strip().splitlines() # # for uri in uris: # # if uri.startswith("file://"): # # path = uri[7:] # strip file:// # # print(f"📄 Dropped file: {path}") # # # TODO: update DPG UI here if you want else: time.sleep(0.01) def dump_tree(w, level=0): indent = " " * level name = w.get_wm_name() print(f"{indent}↳ {hex(w.id)} name: {name}") for child in w.query_tree().children: dump_tree(child, level+1) dpg.create_context() dpg.create_viewport(title='Drag-and-Drop-Example', width=400, height=300) with dpg.window(label="main-window", tag="main-window", no_close=True): dpg.add_text("🗂 Drag a file from Nautilus here!") dpg.setup_dearpygui() dpg.show_viewport() time.sleep(2) # dpg.set_primary_window("main-window", True) d = display.Display() win = _func_find_window_by_name("Drag-and-Drop-Example", d) if not win: raise Exception("Could not find DPG window") print(f"✅ Found Parent window: {hex(win.id)}") print("🔎 Dumping window tree:") dump_tree(win) win = find_real_content_window(win) _func_setup_xdnd(win, d) threading.Thread(target=_func_xdnd_loop, args=(win, d), daemon=True).start() while dpg.is_dearpygui_running(): dpg.render_dearpygui_frame() dpg.destroy_context()
问题分析与修复方案
核心问题
- 未订阅XDND事件:仅设置
XdndAware属性不足以接收事件,需明确向窗口订阅ClientMessage等事件掩码。 - XDND协议交互不完整:未按照协议规范回复拖拽源的
XdndEnter、XdndPosition事件,导致拖拽源认为窗口不支持放置。 - 事件处理逻辑被注释:即使有事件触发,也无法解析拖拽内容。
修复后的代码
import dearpygui.dearpygui as dpg import threading import time from Xlib import X, display, Xatom def _func_find_window_by_name(name, d): root = d.screen().root def search(w): for child in w.query_tree().children: try: win_name = child.get_wm_name() if win_name and name in win_name: return child result = search(child) if result: return result except: pass return None return search(root) def _func_setup_xdnd(win, d): # 设置XdndAware属性,声明支持XDND协议版本5 XdndAware = d.intern_atom("XdndAware") win.change_property(XdndAware, Xatom.ATOM, 32, [5]) # 订阅必要的事件掩码,确保能接收XDND相关事件 win.select_input(X.ClientMessageMask | X.SubstructureNotifyMask) d.flush() # 验证属性设置 prop = win.get_full_property(XdndAware, Xatom.ATOM) if prop: print(f"✅ XdndAware property set correctly: {prop.value}") print(f"✅ Listening on window: {hex(win.id)}") else: print("❌ Failed to set XdndAware property.") def _func_xdnd_loop(win, d): XdndEnter = d.intern_atom("XdndEnter") XdndPosition = d.intern_atom("XdndPosition") XdndDrop = d.intern_atom("XdndDrop") XdndSelection = d.intern_atom("XdndSelection") XdndFinished = d.intern_atom("XdndFinished") UTF8_STRING = d.intern_atom("UTF8_STRING") print(f"👂 Listening for XDND events on: {hex(win.id)}") while dpg.is_dearpygui_running(): if d.pending_events(): e = d.next_event() if e.type == X.ClientMessage: if e.client_type == XdndEnter: print("📥 Drag entered") # 回复拖拽源,确认接收进入事件 reply = d.intern_atom("XdndPosition") win.send_event( e.data[0][1], X.ClientMessageEvent( type=X.ClientMessage, window=e.data[0][1], client_type=reply, data=(32, [win.id, X.CurrentTime, 0, 0, 0]) ) ) elif e.client_type == XdndPosition: print("📍 Drag position updated") # 回复拖拽源,允许放置 reply = d.intern_atom("XdndStatus") win.send_event( e.data[0][1], X.ClientMessageEvent( type=X.ClientMessage, window=e.data[0][1], client_type=reply, data=(32, [win.id, 1, 0, 0, 0]) ) ) elif e.client_type == XdndDrop: print("📦 Drop detected!") # 请求获取拖拽内容 win.convert_selection(XdndSelection, UTF8_STRING, X.CurrentTime) elif e.client_type == XdndFinished: print("🔚 Drag session finished") elif e.type == X.SelectionNotify: if e.property != X.None: prop = win.get_full_property(e.property, UTF8_STRING) if prop: uris = prop.value.decode("utf-8").strip().splitlines() for uri in uris: if uri.startswith("file://"): path = uri[7:] print(f"📄 Dropped file: {path}") # 更新DPG UI显示拖拽结果 dpg.set_value("drop-result", f"已拖拽文件:{path}") # 删除临时属性 win.delete_property(e.property) time.sleep(0.001) dpg.create_context() dpg.create_viewport(title='Drag-and-Drop-Example', width=400, height=300) with dpg.window(label="main-window", tag="main-window", no_close=True): dpg.add_text("🗂 Drag a file from Nautilus here!") dpg.add_text("", tag="drop-result") # 用于显示拖拽结果 dpg.setup_dearpygui() dpg.show_viewport() # 等待视口完全初始化 time.sleep(1) d = display.Display() win = _func_find_window_by_name("Drag-and-Drop-Example", d) if not win: raise Exception("Could not find DPG window") print(f"✅ Found window: {hex(win.id)}") _func_setup_xdnd(win, d) # 启动XDND监听线程 threading.Thread(target=_func_xdnd_loop, args=(win, d), daemon=True).start() while dpg.is_dearpygui_running(): dpg.render_dearpygui_frame() dpg.destroy_context()
关键修复点
- 添加事件订阅:调用
win.select_input()订阅X.ClientMessageMask,确保窗口能接收XDND相关客户端消息。 - 完善协议交互:按照XDND规范添加
XdndEnter、XdndPosition的回复逻辑,让拖拽源确认窗口支持放置。 - 恢复事件处理:取消注释事件解析代码,实现拖拽文件路径的提取,并更新DearpyGUI界面显示结果。
- 优化线程循环:将线程循环条件改为
dpg.is_dearpygui_running(),确保线程随程序退出而终止。
内容的提问来源于stack exchange,提问作者kalopseeia
相关产品推荐
相关产品推荐

