標籤:
問題:
公司項目考慮到跨平台一直都是用qt做,由於項目需求,項目上要增加一個二維碼掃描功能,在安卓可以用QVideoProbe實現抓取網路攝影機視訊框架,用QZxing解碼圖片,從而實現二維碼掃描,但是在ios上,QVideProbe並不支援,所以只好選擇其他抓取視訊框架的方法,考慮使用OPencv實現抓取視訊框架,但是在查看ios文檔時,ios7 以上直接支援二維碼掃描功能,所以放棄使用opencv抓取 + zxing解碼的方法.從而採取ios官方提供的二維碼解碼功能.
實現:
由於我們項目ui一直是用qml實現,但是要實現掃描二維碼功能,需要調用AVFoundation中的方法,同時要顯示ios中的ui顯示網路攝影機及返回qml 鍵.所以這裡需要結合oc 和 qt編程.
直接上代碼:
pro檔案增加
ios {
OBJECTIVE_SOURCES += IOSView.mm \ # object c++ file
IOSCamera.mm
HEADER += IOSView.h \
IOSCamera.h \
IOSCameraViewProtocol.h
QMAKE_LFLAGS += -framework AVFoundation #add AVfoundation framework
QT += gui private
}
重新qmake產生xcode project
IOSView.#include <QQuickItem>
class IOSView : public QQuickItem{ Q_OBJECT Q_PROPERTY(QString qrcodeText READ qrcodeText WRITE setQrcodeText NOTIFY qrcodeTextChanged) public: explicit IOSView(QQuickItem *parent = 0); QString qrcodeText() { return m_qrcodeText; } void setQrcodeText(QString text){ m_qrcodeText = text; emit qrcodeTextChanged(); } QString m_qrcodeText; public slots: void startScan(); //for open ios camera scan and ui private: void *m_delegate; //for communication with qt signals: void qrcodeTextChanged(); void stopCameraScan(); //show qml};
IOSView..mm
#include <UIKit/UIKit.h>#include <QtQuick>#include <QtGui>#include <QtGui/qpa/qplatformnativeinterface.h>#include "IOSView.h"#include "IOSCamera.h"@interface IOSCameraDelegate : NSObject <IOSCameraProtocol> { IOSView *m_iosView;}@end@implementation IOSCameraDelegate- (id) initWithIOSCamera:(IOSView *)iosView{ self = [super init]; if (self) { m_iosView = iosView; } return self;}-(void) scanCancel{ emit m_iosView->stopCameraScan();}-(void) scanResult :(NSString *) result{ m_iosView->setQrcodeText(QString::fromNSString(result));}@endIOSView::IOSView(QQuickItem *parent) : QQuickItem(parent), m_delegate([[IOSCameraDelegate alloc] initWithIOSCamera:this]){} void IOSView::startScan(){ // Get the UIView that backs our QQuickWindow: UIView *view = static_cast<UIView *>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("uiview", window())); UIViewController *qtController = [[view window] rootViewController]; IOSCamera *iosCamera = [[[IOSCameraView alloc] init ]autorelease]; iosCamera.delegate = (id)m_delegate; // Tell the imagecontroller to animate on top: [qtController presentViewController:iosCamera animated:YES completion:nil]; [iosCamera startScan];}
IOSCameraViewProtocol.h
#import <Foundation/Foundation.h>@protocol CameraScanViewProtocol <NSObject>@required-(void) scanCancel;-(void) scanResult :(NSString *) result;@end
IOSCamera.h
#import <UIKit/UIKit.h>#import <AVFoundation/AVFoundation.h>#import "CameraViewProtocol.h"@interface IOSCamera : UIViewController <AVCaptureMetadataOutputObjectsDelegate>{ id<CameraScanViewProtocol> delegate;}@property (retain, nonatomic) IBOutlet UIView *viewPreview;- (IBAction)backQtApp:(id)sender;-(void) startScan;@property (retain) id<CameraScanViewProtocol> delegate;@end
IOSCamera.cpp#import "IOSCamera.h"
@interface IOSCamera ()@property (nonatomic,strong) AVCaptureSession * captureSession;@property (nonatomic,strong) AVCaptureVideoPreviewLayer * videoPreviewLayer;-(BOOL) startReading;-(void) stopReading;-(void) openQtLayer;@end@implementation CameraScanView@synthesize delegate; //Sync delegate for interactive with qt- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. // Initially make the captureSession object nil. _captureSession = nil; // Set the initial value of the flag to NO. _isReading = NO; // Begin loading the sound effect so to have it ready for playback when it‘s needed. [self loadBeepSound];
}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}/*#pragma mark - Navigation- (void)dealloc { [_viewPreview release]; [super dealloc];}- (void)viewDidUnload { [self setViewPreview:nil]; [super viewDidUnload];}- (IBAction)backQtApp:(id)sender { [delegate scanCancel];
[self stopReading];
}
-(void) openQtLayer{
// Bring back Qt‘s view controller:
UIViewController *rvc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
[rvc dismissViewControllerAnimated:YES completion:nil];
}
-(void) startScan{
if (!_isReading) {
// This is the case where the app should read a QR code when the start button is tapped.
if ([self startReading]) {
// If the startReading methods returns YES and the capture session is successfully
// running, then change the start button title and the status message.
NSLog(@"Start Reading !!");
}
}
}
#pragma mark - Private method implementation
- (BOOL)startReading {
NSError *error;
// Get an instance of the AVCaptureDevice class to initialize a device object and provide the video
// as the media type parameter.
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
if (!input) {
// If any error occurs, simply log the description of it and don‘t continue any more.
NSLog(@"%@", [error localizedDescription]);
return NO;
}
// Initialize the captureSession object.
_captureSession = [[AVCaptureSession alloc] init];
// Set the input device on the capture session.
[_captureSession addInput:input];
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
[_captureSession addOutput:captureMetadataOutput];
// Create a new serial dispatch queue.
dispatch_queue_t dispatchQueue;
dispatchQueue = dispatch_queue_create("myQueue", NULL);
[captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
[captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
// Initialize the video preview layer and add it as a sublayer to the viewPreview view‘s layer.
_videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
[_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[_videoPreviewLayer setFrame:_viewPreview.layer.bounds];
[_viewPreview.layer addSublayer:_videoPreviewLayer];
// Start video capture.
[_captureSession startRunning];
_isReading = YES;
return YES;
}
-(void)stopReading{
// Stop video capture and make the capture session object nil.
[_captureSession stopRunning];
_captureSession = nil;
// Remove the video preview layer from the viewPreview view‘s layer.
[_videoPreviewLayer removeFromSuperlayer];
_isReading = NO;
[self openQtLayer];
}
#pragma mark - AVCaptureMetadataOutputObjectsDelegate method implementation
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
// Check if the metadataObjects array is not nil and it contains at least one object.
if (metadataObjects != nil && [metadataObjects count] > 0) {
// Get the metadata object.
AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
// If the found metadata is equal to the QR code metadata then update the status label‘s text,
// stop reading and change the bar button item‘s title and the flag‘s value.
// Everything is done on the main thread.
[delegate scanResult:[metadataObj stringValue]]; //send scan result to qt show
[self performSelectorOnMainThread:@selector(stopReading) withObject:nil waitUntilDone:NO];
_isReading = NO;
}
}
}
@end
OK.大概流程就這些了,添加xib檔案等就不介紹了.
qt for ios掃描二維碼功能實現