Qt(通过公共Lisp';的qtools):QLineEdit不会在单击时激活,而是在选项卡上接收焦点 问题

Qt(通过公共Lisp';的qtools):QLineEdit不会在单击时激活,而是在选项卡上接收焦点 问题,qt,widget,common-lisp,sbcl,ubuntu-16.04,Qt,Widget,Common Lisp,Sbcl,Ubuntu 16.04,我遇到了一个相当奇怪的问题:QLineEdit界面中的对象无法通过鼠标单击来访问,但使用选项卡,它们接收焦点并响应键盘输入。但即使他们有焦点,他们也不会对点击做出反应。更奇怪的是,最后添加的QLineEdit确实会对点击做出响应 额外的问题:鼠标无法访问的线编辑不会显示工具提示 我正在使用CommonLisp构建接口,所以我必须解释一下它是如何工作的。简而言之,我在循环中一个接一个地添加行编辑,所有行编辑都以相同的方式创建并添加到QGridLayout 已更新:有关问题的可能来源,请参见下文 参

我遇到了一个相当奇怪的问题:
QLineEdit
界面中的对象无法通过鼠标单击来访问,但使用
选项卡
,它们接收焦点并响应键盘输入。但即使他们有焦点,他们也不会对点击做出反应。更奇怪的是,最后添加的
QLineEdit
确实会对点击做出响应

额外的问题:鼠标无法访问的线编辑不会显示工具提示

我正在使用CommonLisp构建接口,所以我必须解释一下它是如何工作的。简而言之,我在循环中一个接一个地添加行编辑,所有行编辑都以相同的方式创建并添加到
QGridLayout

已更新:有关问题的可能来源,请参见下文

参数小部件 参数小部件用于表示一个参数输入。每个参数都有名称、值和度量单位。名称和单位显示为标签,该值可编辑,由
QLineEdit

(define-widget parameter-widget (QWidget)
  ((parameter :initarg :parameter)))

(define-subwidget (parameter-widget label) (q+:make-qlabel parameter-widget)
  (setf (q+:text label) (parameter-base-name parameter)))

(define-subwidget (parameter-widget entry) (q+:make-qlineedit parameter-widget)
  (setf (q+:alignment entry) +align-right+)
  (setf (q+:text entry) (format nil "~A" (parameter-value parameter)))
  (setf (q+:tool-tip entry) (parameter-base-description parameter)))

(define-subwidget (parameter-widget units) (q+:make-qlabel parameter-widget)
  (setf (q+:text units) (parameter-units parameter)))
为指示参数值已更改,
参数小部件
将显示新信号
参数已更改
(对于Qt,它被转换为信号“parameterChanged”):

行编辑的插槽尝试将文本转换为数字,如果成功,则更新基础参数并发出
参数更改

(define-slot (parameter-widget parameter-changed) ((new-text string))
  (declare (connected entry (text-changed string)))
  (handler-case
      (let ((new-number (parse-number new-text)))
        (setf (parameter-value parameter) new-number)
        (signal! parameter-widget (parameter-changed)))
    (error nil)))
此方法仅用于自动为
参数
类型的对象构造小部件(工厂方法):

此方法用于对齐
参数容器中多个单个参数的标签(请参阅下文):

参数容器 因此,如果我单独使用
参数小部件
,一切都正常。但大多数情况下,我需要以
参数容器
的形式提供多个参数:

(define-widget parameter-container-widget (QWidget)
  ((parameter-container :initarg :parameter-container)))
(defmethod add-to-grid ((widget parameter-container-widget) grid row column)
  (q+:add-widget grid widget row column 1 1))
此插槽从所有子
参数
捕获信号
参数已更改
,并为容器重新发送该信号

(define-slot (parameter-container-widget parameter-changed) ()
  (format t "~&Re-emitting the signal...~%")
  (signal! parameter-container-widget (parameter-changed)))
layout
QGridLayout
用于为所有
parameter
-参数容器的子对象放置所有单个
参数小部件
。它所做的一切:

  • 它在所有单个参数上循环
  • 为每个组件构造
    参数小部件
  • 逐行添加到布局中
  • 并将信号<代码>参数更改<代码>连接到上述定义的插槽
代码:

此方法用于统一处理
参数小部件
参数容器

(define-widget parameter-container-widget (QWidget)
  ((parameter-container :initarg :parameter-container)))
(defmethod add-to-grid ((widget parameter-container-widget) grid row column)
  (q+:add-widget grid widget row column 1 1))
和泛型实例化器(工厂方法):

系统信息 Qt4.8,通用Lisp:SBCL 1.3.7,来自Quicklisp的qtools,操作系统:Ubuntu 16.04

更新 Windows 10上也存在同样的问题

更新2:完整的最小示例 我很抱歉,上面的代码使用了其他包中的许多定义:全部放进去会让它变得很长。最小的例子可以归结为:

(ql:quickload '(:qtools :qtcore :qtgui))

(defpackage :qtexample
  (:use #:cl+qt))

(in-package qtexample)

(in-readtable :qtools)


(defclass parameter ()
  ((name :initarg :name :accessor parameter-name)
   (value :initarg :value :accessor parameter-value)
   (units :initarg :units :accessor parameter-units)))

(defun parameter (name value units)
  (make-instance 'parameter
    :name name
    :value value
    :units units))

(defvar *mass* (parameter "mass" 1d0 "kg"))
(defvar *velocity* (parameter "velocity" 0.5d0 "m/s"))

(defvar *temperature* (parameter "temperature" 300d0 "K"))
(defvar *pressure* (parameter "pressure" 1d5 "Pa"))

(define-widget parameter-widget (QWidget)
  ((parameter
    :initarg :parameter
    :accessor parameter-widget-parameter)))

(define-subwidget (parameter-widget label)
    (q+:make-qlabel parameter-widget)
  (setf (q+:text label) (parameter-name parameter)))

(defmethod make-ui ((object parameter))
  (make-instance 'parameter-widget :parameter object))

(defconstant +align-right+ 2)

(define-subwidget (parameter-widget entry)
    (q+:make-qlineedit parameter-widget)
  (setf (q+:alignment entry) +align-right+)
  (setf (q+:text entry) (format nil "~A" (parameter-value parameter))))

(define-subwidget (parameter-widget units)
    (q+:make-qlabel parameter-widget)
  (setf (q+:text units) (parameter-units parameter)))

(define-signal (parameter-widget parameter-changed) ())

(define-slot (parameter-widget entry) ((new-text string))
  (declare (connected entry (text-changed string)))
  (format t "~&Parameter has changed~%")
  (handler-case
      (let ((new-number (parse-number:parse-number new-text)))
        (setf (parameter-value parameter) new-number)
        (signal! parameter-widget (parameter-changed)))
    (error nil)))

(defmethod add-to-grid ((widget parameter-widget) grid row column)
  (with-slots (label entry units) widget
    (q+:add-widget grid label row column 1 1)
    (q+:add-widget grid entry row (1+ column) 1 1)
    (q+:add-widget grid units row (+ column 2) 1 1)
    (list (1+ row) (+ column 3))))

(define-widget parameter-widget-window (QWidget)
  ((parameter :initarg :parameter)))


(define-subwidget (parameter-widget-window parameter-widget)
    (make-ui parameter))

(define-subwidget (parameter-widget-window grid)
    (q+:make-qgridlayout  parameter-widget-window)
  (add-to-grid parameter-widget grid 0 0))

(defun parameter-example (parameter)
  (with-main-window
      (window (make-instance 'parameter-widget-window
                :parameter parameter))))

(define-widget parameter-container-widget (QWidget)
  ((parameter-container
    :initarg :parameter-container
    :accessor parameter-container-widget-parameter-container)))

(defmethod make-ui ((object list))
  (make-instance 'parameter-container-widget :parameter-container object))

(define-slot (parameter-container-widget parameter-changed) ()
  (format t "~&Re-emitting the signal...~%")
  (signal! parameter-container-widget (parameter-changed)))

(define-subwidget (parameter-container-widget layout)
    (q+:make-qgridlayout parameter-container-widget)
  (let* ((parameter-widgets (loop for p in parameter-container
                               collect (make-ui p))))
    (loop for p in parameter-widgets
       for row from 0
       do (progn
            (setf (q+:parent p) parameter-container-widget)
            (add-to-grid p layout row 0)
            (connect! p
                      (parameter-changed)
                      parameter-container-widget
                      (parameter-changed))))))

(define-widget parameter-container-widget-window (QWidget)
  ((parameter-container :initarg :parameter-container)))

(define-subwidget (parameter-container-widget-window container-widget)
    (make-ui parameter-container)
  (setf (q+:parent container-widget) parameter-container-widget-window))

(define-slot (parameter-container-widget-window parameter-changed) ()
  (declare (connected container-widget (parameter-changed)))
  (format t "~&Got parameter changed~%"))


(defmethod add-to-grid ((widget parameter-container-widget) grid row column)
  (q+:add-widget grid widget row column))

(defun example-parameter-container (parameter-container)
  (with-main-window
      (window (make-instance 'parameter-container-widget-window
                :parameter-container parameter-container))))

;; to run:
(example-parameter-container (list *mass* *velocity*))
在研究过程中,我偶然发现了一个可能的解决方案。这条线

(setf (q+:parent p) parameter-container-widget) 
将子小部件
p
(在子小部件列表中)的父级设置为
参数容器小部件
。如果这一行被注释,一切正常


p
的子窗口小部件(包括
entry
,一个
QLineEdit
的实例)稍后会添加到网格中,而不是
p
本身!在某种程度上,
参数小部件
不是一个合适的小部件:它只是一个其他小部件的集合,具有如何将它们添加到容器中的规则。但从某种意义上说,它需要充当小部件,能够接收和发送信号。

在@KubaOber和@Shinmera提出建议后,我设法解决了这个问题。我在这里发布修复程序,供将来参考

准备工作没有改变(除了,我忘了添加
:PARSE-NUMBER
系统):

由于
PARAMETER-WIDGET
实际上不是一个WIDGET,而是其他WIDGET的容器(它本身不能添加到一个合适的WIDGET容器中-否则标签的对齐会中断-请参见
ADD-TO-GRID
方法),但它需要能够接收和发送信号,它继承自
QObject
,而不是
QWidget
。所有子部件现在都只是其中的普通类插槽,而不是由
(DEFINE-SUBWIDGET…
定义。注意,没有为任何子小部件提供父部件:当这些小部件添加到容器小部件时,将分配parent属性

(define-widget parameter-widget (QObject)
  ((parameter
    :initarg :parameter
    :accessor parameter-widget-parameter)
   (label :initform (q+:make-qlabel))
   (entry :initform (q+:make-qlineedit))
   (units :initform (q+:make-qlabel))))
初始化进入
INITIALIZE-INSTANCE:AFTER
方法:

(defconstant +align-right+ 2)

(defmethod initialize-instance :after ((object parameter-widget) &key)
  (with-slots (parameter label entry units) object
    (setf (q+:text label) (parameter-name parameter))
    (setf (q+:text entry) (format nil "~A" (parameter-value parameter)))
    (setf (q+:alignment entry) +align-right+)
    (setf (q+:text units) (parameter-units parameter))))
定义的其余部分保持不变(信号与问题基本无关,但有助于理解我为什么要跳过所有这些障碍):

这是一个简单的演示,所有的东西都只适用于一个参数。现在,
PARAMETER-WIDGET
不再是一个小部件,因此它被添加为一个类槽

(define-widget parameter-widget-window (QWidget)
  ((parameter :initarg :parameter)
   (parameter-widget)))
窗口的网格:

(define-subwidget (parameter-widget-window grid)
    (q+:make-qgridlayout  parameter-widget-window))
此处,将
PARAMETER-WIDGET
组件添加到网格中的内容从该定义中移除。原因:此时插槽
PARAMETER-WIDGET
未绑定。它在
INITIALIZE-INSTANCE:AFTER
方法中绑定,其中所有组件都添加到网格中:

(defmethod initialize-instance :after ((object parameter-widget-window) &key)
  (with-slots (parameter parameter-widget grid) object
    (setf parameter-widget (make-ui parameter))
    (setf (q+:parent parameter-widget) object)
    (add-to-grid parameter-widget grid 0 0)))
发射器保持不变:

(defun parameter-example (parameter)
  (with-main-window
      (window (make-instance 'parameter-widget-window
                :parameter parameter))))
要运行:
(参数示例*mass*)

PARAMETER-CONTAINER-WIDGET
是一个合适的小部件。它的定义没有改变:

(define-widget parameter-container-widget (QWidget)
  ((parameter-container
    :initarg :parameter-container
    :accessor parameter-container-widget-parameter-container)))

(defmethod make-ui ((object list))
  (make-instance 'parameter-container-widget :parameter-container object))

(define-slot (parameter-container-widget parameter-changed) ()
  (format t "~&Re-emitting the signal...~%")
  (signal! parameter-container-widget (parameter-changed)))
(define-widget parameter-container-widget-window (QWidget)
  ((parameter-container :initarg :parameter-container)))

(define-subwidget (parameter-container-widget-window container-widget)
    (make-ui parameter-container)
  (setf (q+:parent container-widget) parameter-container-widget-window))

(define-slot (parameter-container-widget-window parameter-changed) ()
  (declare (connected container-widget (parameter-changed)))
  (format t "~&Got parameter changed~%"))

(defun example-parameter-container (parameter-container)
  (with-main-window
      (window (make-instance 'parameter-container-widget-window
                :parameter-container parameter-container))))
布局
定义也不会改变。但是现在可以安全地将
p
PARAMETER-WIDGET
)的父属性设置为
PARAMETER-WIDGET-CONTAINER
,因此当容器被销毁时,它会被销毁

(define-subwidget (parameter-container-widget layout)
    (q+:make-qgridlayout parameter-container-widget)
  (let* ((parameter-widgets (loop for p in parameter-container
                               collect (make-ui p))))
    (loop for p in parameter-widgets
       for row from 0
       do (progn
            (add-to-grid p layout row 0)
            (let ((pp p))
              (setf (q+:parent pp) parameter-container-widget))
            (connect! p
                      (parameter-changed)
                      parameter-container-widget
                      (parameter-changed))))))
将此小部件添加到网格很简单(但不完全正确,但可以修复,请参阅下文):

演示部分没有更改:

(define-widget parameter-container-widget (QWidget)
  ((parameter-container
    :initarg :parameter-container
    :accessor parameter-container-widget-parameter-container)))

(defmethod make-ui ((object list))
  (make-instance 'parameter-container-widget :parameter-container object))

(define-slot (parameter-container-widget parameter-changed) ()
  (format t "~&Re-emitting the signal...~%")
  (signal! parameter-container-widget (parameter-changed)))
(define-widget parameter-container-widget-window (QWidget)
  ((parameter-container :initarg :parameter-container)))

(define-subwidget (parameter-container-widget-window container-widget)
    (make-ui parameter-container)
  (setf (q+:parent container-widget) parameter-container-widget-window))

(define-slot (parameter-container-widget-window parameter-changed) ()
  (declare (connected container-widget (parameter-changed)))
  (format t "~&Got parameter changed~%"))

(defun example-parameter-container (parameter-container)
  (with-main-window
      (window (make-instance 'parameter-container-widget-window
                :parameter-container parameter-container))))
运行:(define-widget parameter-container-widget (QWidget) ((parameter-container :initarg :parameter-container :accessor parameter-container-widget-parameter-container))) (defmethod make-ui ((object list)) (make-instance 'parameter-container-widget :parameter-container object)) (define-slot (parameter-container-widget parameter-changed) () (format t "~&Re-emitting the signal...~%") (signal! parameter-container-widget (parameter-changed)))
(define-subwidget (parameter-container-widget layout)
    (q+:make-qgridlayout parameter-container-widget)
  (let* ((parameter-widgets (loop for p in parameter-container
                               collect (make-ui p))))
    (loop for p in parameter-widgets
       for row from 0
       do (progn
            (add-to-grid p layout row 0)
            (let ((pp p))
              (setf (q+:parent pp) parameter-container-widget))
            (connect! p
                      (parameter-changed)
                      parameter-container-widget
                      (parameter-changed))))))
(defmethod add-to-grid ((widget parameter-container-widget) grid row column)
  (q+:add-widget grid widget row column))
(define-widget parameter-container-widget-window (QWidget)
  ((parameter-container :initarg :parameter-container)))

(define-subwidget (parameter-container-widget-window container-widget)
    (make-ui parameter-container)
  (setf (q+:parent container-widget) parameter-container-widget-window))

(define-slot (parameter-container-widget-window parameter-changed) ()
  (declare (connected container-widget (parameter-changed)))
  (format t "~&Got parameter changed~%"))

(defun example-parameter-container (parameter-container)
  (with-main-window
      (window (make-instance 'parameter-container-widget-window
                :parameter-container parameter-container))))
(example-parameter-container (list *mass* *velocity* (list *temperature* *pressure*)))