xricht17 / twpp

TWAIN framework for C++11. Makes development of applications and data sources much easier.
MIT License
39 stars 17 forks source link

Application Sample Code #27

Open wrmbaron opened 4 years ago

wrmbaron commented 4 years ago

Hi, would it be possible to add a minimal sample application to show how to get a list of sources and to set up your connection to a data source (maybe without using namespace)?

xricht17 commented 4 years ago

Hi, the README itself already contains a sort of minimal sample application.

Is there something in particular that you have trouble with? We can improve that! :wink:

wrmbaron commented 4 years ago

Thanks for the good work, but the README only just touches the tip of the iceberg. There is a lot of story behind i.e. the capabilities. I tried to list all the values and have come up with the following, clumsy way:

std::vector<Twpp::Source> sources;
Twpp::Source              defaultSource;
Twpp::Manager             manager {Twpp::Identity {
        Twpp::Version {1, 0, Twpp::Language::English, Twpp::Country::UnitedKingdom, "v0.9"},
        Twpp::DataGroup::Image,
        "Fake Manufacturer",
        "Fake product family",
        "Fake product name"}};

manager.load();
manager.open();
manager.sources(sources);
manager.defaultSource(defaultSource);
if (!sources.empty()) {
    Twpp::Source &         source {sources.front()};
    const Twpp::Identity & identity {source.identity()};

    std::cout << "product: " << identity.productName().string()
              << " manufacturer: " << identity.manufacturer().string() << std::endl;
    if (Twpp::success(source.open())) {
        Twpp::Capability supported {Twpp::CapType::SupportedCaps};

        if (Twpp::success(source.capability(Twpp::Msg::Get, supported))) {
            for (const Twpp::CapType capType: supported.data<Twpp::CapType::SupportedCaps>()) {
                std::cout << "cap type: " << static_cast<uint16_t>(capType) << std::endl;

                // just one case now, but more to come ...
                switch (capType) {
                case Twpp::CapType::IBitDepth: {
                    Twpp::Cap<Twpp::CapType::IBitDepth> bitDepth;

                    if (Twpp::success(source.capability(Twpp::Msg::Get, bitDepth))) {
                        std::cout << "current bitDepth: " << bitDepth.currentItem()
                                  << std::endl;
                    }
                    break;
                }
                default:
                    break;
                }
            }
        } else {
            std::cerr << "DataSource does not return capabilities";
        }
    } else {
        std::cerr << "TWAIN DataSource does not open" << std::endl;
    }
} else {
    std::cerr << "No TWAIN data sources" << std::endl;
}

Creating an own Cap variable for each capability really hurts and bloats the code. Some examples for handling all the different data types and setting values would be helpful, but if not, I'll just dig my way through. Some way to do something like std::cout << source.getCapability(Twpp::CapType::IBitDepth).getCurrentValue(); would be really cute, but I guess to get rid of that complicated TWAIN type system, you would need something like QVariant of Qt.

So do you have more example code for applications or a URL of an application, that uses Twpp?

xricht17 commented 4 years ago

There are two options for capability handling - Capability and Cap.

Capability is the more generic one. It can hold any capability, data, and container type combinations - it is a sort of QVariant for capabilities as you mention. You need to know the data and container types only at the very moment you access the data, not before. This option is ideal for bulk operations, where you do not really care about each single capability, like enumerating capabilities and their data - that's what you seem to be doing.

On the other hand, Cap guarantees that it is either empty or it holds only a certain capability type and its proper data type. Cap would be best used if you want to control a specific capability. E.g. you want to query or set transfer mechanism - it tells you exactly what data type you are going to get. With Capability, you need to know that yourself, and make sure it is actually there.

I am quite sure enabling something like source.getCapability(Twpp::CapType::IBitDepth).getCurrentValue() could be done, but as another layer over TWPP. TWPP is meant to remove the burden of managing the TWAIN boilerplate (object-oriented approach, dynamic memory, handling states, type safety) while staying as close to TWAIN API as possible.

I am afraid nobody has advertised any application that would use TWPP so far.

Regarding your code:

wrmbaron commented 4 years ago

Thank you for your clarifications. The one about defaultSource() was very helpful, I had the wrong impression there. That's one of the many reasons I never use non const reference parameters (either const reference or pointer, but I even try to avoid that and return multiple values through a more complex result type like presented in https://stackoverflow.com/questions/321068/returning-multiple-values-from-a-c-function unless there are severe performance reasons but then I would benchmark before doing read-write parameters).

I will try out using Cap directly, but I have the impression that would call the TWAIN api each time and be a little inefficient?

Now that I find out I am a sort of pioneer for using Twpp for applications, I will keep you posted on how I apply your code in mine and if and when I create anything useful with it, I will probably make it public.

Regarding my code:

netpipe commented 4 years ago

https://github.com/tecan/QTScanner i started this repo with twpp folder, hopefully someone can fix it. has qt interface ready to use

pdgmdm commented 4 years ago

Hi, Would it be possible to extend the sample/documentation to show how to use/integrate the Twpp::Source::processEvent in a Qt application ? I would like to create a GUI application that launches the scanner ds ui without blocking the main application. If possible, also explain how to support multiple image transfers ? Many thanks!!

xricht17 commented 4 years ago

Hi pdgmdm,

The glue between Qt and TWPP is done using native event filters. In case of Qt5, one would implement their own QAbstractNativeEventFilter and register its instance in QAbstractEventDispatcher like so QAbstractEventDispatcher::instance()->installNativeEventFilter(<the filter instance>); once for the whole application. When connected to an enabled source, the filter would pass events to it using Source#processEvent.

As for multiple image transfer, I am afraid I will have to refer you to the TWAIN Specification. They have a nice chapter Transfer of Multiple Images that you should have a look at after you get yourself familiar with single image transfer process. The specification naturally mentions vanilla TWAIN operations, but you should be able to translate them to TWPP without much difficulty.

pdgmdm commented 4 years ago

Thanks for this input. Now I can show the UI of the DS (I currently use a scanner simulator https://developer.dynamsoft.com/dwt/kb/2659). However, when clicking the scan-button, the cancel-button, or the close-button in the titlebar, the UI is not closed. I do get the ReturnCode::Success of Cancel from the Source::processEvent method, but this does not close the scanners' UI. I also called Twpp::Source::disable, but this also does not close the UI. Could you explain how this could be done ?

xricht17 commented 4 years ago

The source will not usually do anything without the application explicitly telling it to do so, or at least it restricts itself to the minimum required independent operation. By pressing the Cancel button, the source only requests to close its GUI, the application must call Source#disable to actually close it.

adam-seychell commented 1 year ago

I have written an application using Qt and twpp that includes ability to scan documents. A custom UI is used to select data source and set some capabilities. Calls to twpp are executed in a separate thread to keep UI responsive. Image transfer is done via buffered memory mode and handles multiple images. If I can see an interest I can make available a minimal example of a Qt application based on the success of what I have done so far.