用 Qt 控制 Nikon 显微镜的电动物镜转盘

最近的一个项目,用到了一台 Nikon 的金相显微镜,并且配了个电动的物镜转盘。为了控制这个电动物镜转盘,我折腾了差不多有4-5天。中间遇到了各种问题,还好,最后都解决了。这里简单纪录了我遇到的各种问题,希望对其他人能有有所帮助。

我的开发环境如下:

win 7 64位操作系统。

qt 5.4.1(32位) + vs2010

Nikon 的物镜转化器通过 USB 接口连接到我的硬件平台。

Nikon 这样的大厂还是比较靠谱的,提供有 LV Series Software Developer’s Kit (SDK)。因为我的操作系统是 64 位的,所以我要了 64 位的 SDK。 要来了 SDK 后发现,Nikon 将他显微镜所有的控制功能都封装到了一个 ActiveX 控件里。它 64 位的 SDK 就只提供了 64 位的 ActiveX 控件。第一个悲剧出现了,我 32 位的应用程序用不了这个 64 位的控件。当然主要还是我水平低,理论来说 ActiveX 控件是可以跨系统调用的,32 位程序用 64 位控件肯定是有办法的。

因为我对 ActiveX 技术了解的太少,这么点小事就折腾了 2 天。后来明白了原因之后又去找 Nikon 要 32 位的 SDK。最后我两个 SDK 都要用到,使用 64 位 SDK 里的 USB 驱动,32 位 SDK 的 COM 组件。

SDK 都准备齐了之后开始写程序。Qt 里有一个模块叫做 “Active Qt”,提供了对 ActiveX 和 COM 组件的支持。这个 “Active Qt” 还是很给力的。用 QAxObject 类就可以访问 COM 组件中的对象。

比如:

    QAxObject *nikon = new QAxObject(parent);
    nikon->setControl("Nikon.LvMic.NikonLV");

等价于 VBScript 脚本:

dim nikonLV
set nikonLV = CreateObject ("Nikon.LvMic.NikonLV")
    QAxObject* nosepiece = nikon->querySubObject("Nosepiece");

就可以获取子对象 “Nosepiece”。到这里都很顺利。

SDK 中 VBScript 语言写的旋转物镜转盘的例子是这样的:

Public Const StatusUnknown = -1
Public Const StatusFalse = 0
Public Const StatusTrue = 1

dim nikonLV
set nikonLV = CreateObject ("Nikon.LvMic.NikonLV")

If (nikonLV.Nosepiece.IsMounted = StatusTrue) Then
    nikonLV.Nosepiece.Forward
End If

我仿照着写的 Qt 代码如下:

    QAxObject *nikon = new QAxObject(parent);
    nikon->setControl("Nikon.LvMic.NikonLV");
    QAxObject* nosepiece = nikon->querySubObject("Nosepiece");
    if(nosepiece->property("IsMounted").toInt() == 1)
    {
        nosepiece->dynamicCall("Forward()");
    }

但是这个代码就不能正常工作,nosepiece->property(“IsMounted”).toInt() 每次的返回值都是 0。

    int getPosition()
    {
        return nosepiece->property("position").toInt();
    }

这个代码的本意是获得物镜转盘当前的位置,但是每次的返回值却都是 0。

这个问题卡了我两天。中间试了各种办法都没有作用。甚至装了个 64 位的 qt 5.7.0。

没有头绪时就反复的读文档。看到 Qt 提供了 dumpdoc 和 dumpcpp 这两个工具之后就想用 dumpcpp 来试试。

用 dumpcpp 倒是生成了相应的头文件和 Cpp 文件。但是加入到项目中编译时报了几百个各种错误,试图修改了几处后还是果断放弃了。

直到今天,用 dumpdoc 生成了NikonLV 和 Nosepiece 这两个对象的文档。仔细读了两遍突然眼前一亮。 文档中列出的 Nosepiece 的属性有这些:

  • QVariant IsMounted;

  • QVariant Position;

  • EnumStatus _IsMounted;

  • int _Position;

这里删掉了其余的属性,就留了四个。看到这里之后就恍然大悟了。原来我应该用带下划线的那些属性。之后我的程序就顺利调通了。

不过为啥会多处这些带下划线的属性呢?在 SDK 的文档中是没有的,VBScript 代码中也没有。希望有高手能给我解释一下。

之后我将相关的代码做了整理,放到了一个类中。下面是这个类的代码,首先是头文件:

#ifndef NIKONNOSEPIECE_H
#define NIKONNOSEPIECE_H

#include <QObject>
#include <QAxObject>
#include <QTimer>

class NikonNosepiece : public QObject
{
    Q_OBJECT
public:
    explicit NikonNosepiece(QObject *parent = 0);
    ~NikonNosepiece();
    bool isMounted();
signals:
    void positionChanged(int n);
public slots:
    void setPosition(int n);
    void forward();
    void reverse();
private slots:
    void on_timer();
private:
    QAxObject *nikon;
    QAxObject *nosepiece;
    QTimer *m_timer;
    int m_pos;
};

#endif // NIKONNOSEPIECE_H

之后是 cpp 文件:

#include "nikonNosepiece.h"
#include <QDebug>

NikonNosepiece::NikonNosepiece(QObject *parent) : QObject(parent)
{
    m_pos = 0;
    nikon = new QAxObject(parent);
    nikon->setControl("Nikon.LvMic.NikonLV");

    qDebug() << "objectName = " << nikon->property("objectName");
    qDebug() << "SystemName = " << nikon->property("SystemName");
    qDebug() << "VersionOfSDK = " << nikon->property("VersionOfSDK");

    nosepiece = nikon->querySubObject("Nosepiece");
    m_timer = new QTimer(this);
    m_timer->setInterval(500);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(on_timer()));
    m_timer->start();
}

void NikonNosepiece::on_timer()
{
    int pos = nosepiece->property("_Position").toInt();
    if (pos != m_pos)
    {
        m_pos = pos;
        if(m_pos != 0)
        {
            emit positionChanged(m_pos);
        }
    }
}

bool NikonNosepiece::isMounted()
{
    return nosepiece->property("_IsMounted").toInt();
}

NikonNosepiece::~NikonNosepiece()
{
    delete nikon;
    m_timer->stop();
}

void NikonNosepiece::setPosition(int n)
{
    int lowerLimit = nosepiece->property("_LowerLimit").toInt();
    int upperLimit = nosepiece->property("_UpperLimit").toInt();
    n = qBound(lowerLimit, n, upperLimit);
    nosepiece->setProperty("_Position", n);
}

void NikonNosepiece::forward()
{
    nosepiece->dynamicCall("Forward()");
}

void NikonNosepiece::reverse()
{
    nosepiece->dynamicCall("Reverse()");
}

代码很简单,就不多解释了。用起来很方便。比如切换到第二个物镜,就下面两行代码:

NikonNosepiece nosepiece;
nosepiece.setPositon(2);

物镜改变时会发出 SIGNAL : void positionChanged(int n);

我们的程序中只需要把这个 signal 链接到合适的 Slot 上就可以了。

希望对大家有用。

用 Qt 控制 Nikon 显微镜的电动物镜转盘的更多相关文章

  1. Qt 控制watchdog app hacking

    /************************************************************************** * Qt 控制watchdog app hack ...

  2. QT 控制LED实验

    1.实验准备 在PC 机D:盘下创建文件夹qt-led,将光盘qt_led_exp 文件夹下的images 文件夹拷贝到E:盘下qt-led 文件夹qt-led 内 2.新建工程 新建一个Empty ...

  3. WiFi QC 自动测试:Qt控制无线路由器

    在测试wifi的时候,测试人员一般要使用很多不同型号的AP,并且需要不断地切换Chariot的配置. 这里的思路是致力于提供一个友好的GUI界面来自动控制AP,并且自动控制Chariot进行Throu ...

  4. Qt 控制线程的顺序执行(使用QWaitCondition,并且线程类的run函数里记得加exec(),使得线程常驻)

    背景项目中用到多线程,对线程的执行顺序有要求: A.一个线程先收数据 B.一个线程处理数据 C.一个线程再将处理后的数据发送出去 要求三个线程按照ABC的顺序循环执行. 思路子类化多线程方法 重写子类 ...

  5. QT控制文本框输入内容

    利用正则表达式,例: //即账号最长为10位,只能由数字组成 QRegExp regx("[0-9]{1,10}"); QValidator *validator = new QR ...

  6. 带UI的小初高数学学习软件—艰难地用C++(QT库)实现的过程

    从互相了解对方的代码思路然后确定用C++编写,到用win32写界面时变得摇摆不定的考虑着要不要改变语言,再到用QT写完界面后发现短信接口一般都不提供C++,最后到QT打包出来的可执行文件在别的设备上无 ...

  7. Dymola — 多学科系统仿真平台

            Dymola 是法国Dassault Systems公司的多学科系统仿真平台,广泛应用于国内外汽车.工业.交通.能源等行业的系统总体架构设计.指标分解以及系统功能验证及优化等.Dymo ...

  8. Qt中利用QTime类来控制时间,这里简单介绍一下QTime的成员函数的用法:

    Qt中利用QTime类来控制时间,这里简单介绍一下QTime的成员函数的用法: ------------------------------------------------------------ ...

  9. Qt小项目之串口助手控制LED

    Qt小项目之串口助手控制LED 前言 最近刚学了一点Qt开发上位机,尝试着做个小软件练练手.查找了很多资料,做了一个简单的串口助手,可以实现串口基本发送和接收功能,支持中文显示,还可以控制STM32开 ...

随机推荐

  1. IOS 集成百度地图

    申请key ● http://lbsyun.baidu.com/apiconsole/key 下载SDK ● 下载百度地图开发包:http://api.map.baidu.com/lbsapi/clo ...

  2. IOS 蓝牙(GameKit、Core Bluetooth)

    GameKit的蓝牙开发注意 ● 只能用于iOS设备之间的连接 ● 只能用于同一个应用程序之间的连接 ● 最好别利用蓝牙发送比较大的数据 /* 关于蓝牙的数据传输  1. 一次性传送,没有中间方法,所 ...

  3. 漫谈C++:良好的编程习惯与编程要点(转载)

    这个博主写的文章真是细腻,全面,严谨,应当多读几回 原文http://www.cnblogs.com/QG-whz/p/5517643.html 阅读目录 以良好的方式编写C++ class Clas ...

  4. windows 网络通讯模型Overlapped (转)(未看)

    https://blog.csdn.net/jofranks/article/details/7895316 https://blog.csdn.net/caoshiying/article/deta ...

  5. 关于bootstrap-table服务端分页问题

    昨天项目中涉及到了前端表格分页问题.数据一共有1万多条,所以选择了后端分页. 之前用的都是前端分页,第一次使用后端分页.网上也找到了一些例子,最后做出来了. 这里用的是bootstrap-table插 ...

  6. Android NDK r8 Cygwin CDT 在window下开发环境搭建 安装配置与使用 具体图文解说

    版权声明:本博客全部文章均为原创.欢迎交流.欢迎转载:转载请勿篡改内容,而且注明出处,谢谢! https://blog.csdn.net/waldmer/article/details/3272500 ...

  7. POJ 2985 名次树

    题意:1~n个猫,有合并操作,有询问操作,合并两只猫所在的集合,询问第K大的集合. 分析:合并操作用并查集,用size维护,询问操作用Treap.注意优化,不能刚开始就把所有size = 1放到名次树 ...

  8. Tinkoff Challenge - Final Round (ABC)

    A题:从两个保安中间那钞票 #include <bits/stdc++.h> using namespace std; int main() { int a,b,c; scanf(&quo ...

  9. react中性能优化的点

    react提升代码性能的点 1.绑定如果改变作用域点话放在constructor里面做,这样可以保证整个程序的作用域绑定操作只会执行一次,而且避免子组件的无谓渲染. 2.内置的setState是个异步 ...

  10. [19/03/16-星期六] 常用类_Date时间类&DateFormat类

    一.Date时间类 计算机中 以1970 年 1 月 1 日 00:00:00定为基准时间,每个度量单位是毫秒(1秒的千分之一) 用ong类型的变量来表示时间,如当前时刻数值:long  now =n ...