compare std::wstring type and represent of wstring literal in ChaiScript - wstring

I want to write ChaiScript code using std::wstring type like below c++ code.
#include <iostream>
int testfunc(std::wstring s, std::wstring t)
{
if(s==t)
{
std::cout << "1" << std::endl;
}
if(s[1]==t[1])
{
std::cout << "2" << std::endl;
}
if(s==L"aaaa")
{
std::cout << "3" << std::endl;
}
if(s[1]==L'b')
{
std::cout << "4" << std::endl;
}
return 5;
}
int main()
{
std::cout << testfunc(std::wstring(L"abcd"), std::wstring(L"abbb"));
return 0;
}
D:\TestWork\test_chaiscript>t6
2
4
5
To compare the instances of std::wstring type is good.
#include <iostream>
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript_stdlib.hpp>
#include <chaiscript/dispatchkit/bootstrap_stl.hpp>
int main()
{
chaiscript::ChaiScript chai(chaiscript::Std_Lib::library());
chai.add(chaiscript::bootstrap::standard_library::string_type<std::wstring>("wstring"));
std::cout << chai.eval<std::function<int (std::wstring, std::wstring)> >(
"fun(s, t){"
" if(s==t){"
" print(\"1\");"
" }"
" return 3;"
"}"
)(std::wstring(L"abcd"), std::wstring(L"abaa"));
return 0;
}
D:\TestWork\test_chaiscript>t5
3
To compare the instances of wchar_t type doesn't work.
Do the compare operator method must be added?
" if(s[1]==t[1]){"
" print(\"2\");"
" }"
D:\TestWork\test_chaiscript>t5
terminate called after throwing an instance of 'chaiscript::exception::eval_error'
what(): Error: "Error with numeric operator calling: =="
To compare the instance of std::wstring type with the literal of string type doesn't work.
I cannot input the literal of wstring type.
Is it possible to input the literal of wstring type in ChaiScript?
" if(s==\"aaaa\"){"
" print(\"2\");"
" }"
D:\TestWork\test_chaiscript>t5
terminate called after throwing an instance of 'chaiscript::exception::eval_error'
what(): Error: "Can not find appropriate '==' operator." With parameters: (wstring, const string)
To compare the instance of wchar_t type with the literal of wchar_t type doesn't work.
I cannot input the literal of wchat_t type.
Is it possible to input the literal of wchat_t type in ChaiScript?
" if(s[1]=='b'){"
" print(\"2\");"
" }"
D:\TestWork\test_chaiscript>t5
terminate called after throwing an instance of 'chaiscript::exception::eval_error'
what(): Error: "Error with numeric operator calling: =="

Yes, you will need to tell ChaiScript something about wchar_t, such as the == operator. For example:
chai.add(
chaiscript::fun<bool (wchar_t, wchar_t)>(
[](wchar_t lhs, wchar_t rhs) { return lhs == rhs; }
), "=="
);

I think that the native support wchar_t need in ChaiScript.
"Error with numeric operator calling: " exception is generated in chaiscript_eval.hpp.
If the type of variable is arithmetic, Boxed_Number::do_oper() is called.
// chaiscript_eval.hpp, line 98
if (t_oper != Operators::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic())
{
// If it's an arithmetic operation we want to short circuit dispatch
try{
return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs);
} catch (const chaiscript::exception::arithmetic_error &) {
throw;
} catch (...) {
throw exception::eval_error("Error with numeric operator calling: " + t_oper_string);
}
} else {
It seems to use std::is_arithmetic to determine whether arithmetic type or not.
// type_info.hpp, 133 line
struct Get_Type_Info
{
typedef T type;
static Type_Info get()
{
return Type_Info(std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value,
std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<T>::type, bool>::value,
&typeid(T),
&typeid(typename Bare_Type<T>::type));
}
};
wchat_t is the arithmetic type.
std::cout << std::is_arithmetic<wchar_t>::value << std::endl;
std::cout << std::is_arithmetic<std::string>::value << std::endl;
D:\TestWork\test_chaiscript>t5.exe
1
0
If the type is arithmetic, oper() method for Boxed_Value is called.
But, wchar_t type is not supported in oper() method.
// boxed_number.hpp, 295 line
inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs)
{
const Type_Info &inp_ = t_lhs.get_type_info();
if (inp_ == typeid(int)) {
return oper_rhs<int>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(double)) {
return oper_rhs<double>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(long double)) {
return oper_rhs<long double>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(float)) {
return oper_rhs<float>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(char)) {
return oper_rhs<char>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(unsigned int)) {
return oper_rhs<unsigned int>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(long)) {
return oper_rhs<long>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(unsigned long)) {
return oper_rhs<unsigned long>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::int8_t)) {
return oper_rhs<std::int8_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::int16_t)) {
return oper_rhs<std::int16_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::int32_t)) {
return oper_rhs<std::int32_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::int64_t)) {
return oper_rhs<std::int64_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::uint8_t)) {
return oper_rhs<std::uint8_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::uint16_t)) {
return oper_rhs<std::uint16_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::uint32_t)) {
return oper_rhs<std::uint32_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(std::uint64_t)) {
return oper_rhs<std::uint64_t>(t_oper, t_lhs, t_rhs);
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
}
I add the code to support wchar_t type like below in all place used inp == typeid(char).
} else if (inp_ == typeid(char)) {
return go<LHS, char>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(wchar_t)) {
return go<LHS, wchar_t>(t_oper, t_lhs, t_rhs);
} else if (inp_ == typeid(unsigned int)) {
My test code works well.
std::cout << chai.eval<std::function<int (wchar_t a, wchar_t b)> >(
"fun(a, b){"
" if(a==b){"
" print(\"2\");"
" }"
" return 3;"
"}"
)(wchar_t(L'a'), wchar_t(L'a'));
D:\TestWork\test_chaiscript>t5.exe
2
3

Related

How can I store head node of a linked list in a function during recursion

I have written a function to output a linked list in reverse order. Is there a way I can store the head node?
void output_rev_backtracking(Node *x) {
Node *t=x;
if(x==NULL) {
return;
} else {
output_rev_backtracking(x->next);
if(x==t) {
cout << x->data << "\n";
} else {
cout << x->data << " ";
}
}
}
This code is wrong since "x" changes to "x->next" in every step. How should I store "x" in "t"
void recursive_reverse(Node* current, Node* new_head){
if(!current)
return;
Node* next = current->next;
if(!new_head){
new_head = current;
new_head->next = null;
}else{
current->next = new_head;
new_head = current;
}
recursive_reverse(next, new_head);
}

Qt::ToolTipRole not getting triggered

I am using a model implemented using QAbstractTableModel. I want to show individual tooltips for all the headers. In the data() and headerData() implementations, however, When I print the role, it is never becoming Qt::ToolTipRole. Is there anything else I have to do to trigger the tooltiprole here?
QVariant jlsGenericReportTableModel::data(const QModelIndex &index, int role) const
{
printf("Role in data() = %d\n",role);
if (role == Qt::DisplayRole)
{
// printf(" --- Returned\n");
if(_dataStore)
{
if(index.row() < _dataStore->RowCount())
{
return _dataStore->DataForTuple(index.row(),index.column());
}
}
else
{
if(index.row() < _rows.size())
{
return _rows.at(index.row())->DataForColumn(index.column());
}
}
}
else if (role == Qt::TextAlignmentRole )
{
if (( (QMetaType::Type)(data(index,Qt::DisplayRole)).type()) == QMetaType::QString)
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
else
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
}
else if (role == Qt::ToolTipRole) {
printf("Triggered\n"); //Doesn't come here
//if (orientation == Qt::Horizontal)
return QString("Horizontal Header No. ");
//else
// return QString("Vertical Header No. ");
}
return QVariant();
}

QUdpSocket::writeDatagram, wait for buffer free

I'm using method QUdpSocket::writeDatagram and sending a lot of data. When the socket is full, the method return -1. Is it possible to do somethind like select() with a QUdpSocket?
while(!counterList.empty())
{
CounterValuePtr value = counterList.dequeue();
if (mUdpSocket->bytesToWrite() <= 1<<13)
{
const QByteArray datagram = toDatagram(*value);
qint64 sentBytes = 0;
try
{
sentBytes = mUdpSocket->writeDatagram(datagram, QHostAddress(mSettings->getConnectionIp()), mSettings->getConnectionPort());
}
catch(...)
{
emit dataLost();
qWarning() << "Exception";
throw;
}
qDebug() << datagram;
if(sentBytes == -1)
{
QString errorMsg = getErrorMsg(WSAGetLastError());
qWarning() << "SocketError (" << mUdpSocket->error() << ") : " << mUdpSocket->errorString() << ". " << errorMsg;
emit dataLost();
}
else if(sentBytes < datagram.size())
{
qWarning() << "Not all data sent";
emit dataLost();
}
else
{
qDebug() << "Counter "<< value->getName() << " sent";
datasent = true;
emit dataSent();
}
}
else
{
qWarning() << "Socket write buffer full. Dropping data.";
emit dataLost();
}
}
Some times, writeDatagram returns -1. When I print the last error with WSAGetLastError it prints the buffer is full.

CutyCapt not able to generate HTTPS web site screenshot when launched from w3wp.exe

I have updated the latest code from SVN and complie CutyCapt with Qt SDK 4.8.
And I have static-linked libeay32.dll / ssleay32.dll in the same location of the binary.
It works fine if I launch the CutyCapt process in command window.
It works fine if I launch the CutyCapt process from ASP.NET (w3wp) to capture HTTP web page.
it does NOT work if I launch the CutyCapt process from ASP.NET (w3wp) to capture HTTPS web page, always generates a blank image.
I doubted it was because the libeay32.dll / ssleay32.dll dependencies are not loaded, so I copied these 2 dll to system32 / SysWOW64, this does not resolve my problem.
Then I monitor the process by ProcessMonitor, it shows me that libeay32.dll / ssleay32.dll are loaded successfully.
So, the dependencies are not the reason.
Here is my C# code to launch the CutyCapt,
using (Process process = new Process())
{
process.StartInfo.FileName = ti.ExeFile;
process.StartInfo.ErrorDialog = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(ti.ExeFile);
process.StartInfo.UseShellExecute = false;
process.StartInfo.Arguments = string.Format(#"--min-width=1024 --delay=1500 --javascript=off --auto-load-images=on --plugins=off --java=off --url={0} --out={1}"
, ti.Url
, destFile
);
process.Start();
process.WaitForExit();
process.Close();
}
Does anyone knows how to allow CutyCapt works from w3wp?
SOLUTION:
I have resolved this problem by modifying the CutyCapt source.
The idea is that, get the HTML of the url then set the HTML to the WebKit.
CutyCapt.hpp
#include <QtWebKit>
class CutyCapt;
class CutyPage : public QWebPage {
Q_OBJECT
public:
void setAttribute(QWebSettings::WebAttribute option, const QString& value);
void setUserAgent(const QString& userAgent);
void setAlertString(const QString& alertString);
void setPrintAlerts(bool printAlerts);
void setCutyCapt(CutyCapt* cutyCapt);
QString getAlertString();
protected:
QString chooseFile(QWebFrame *frame, const QString& suggestedFile);
void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID);
bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result);
void javaScriptAlert(QWebFrame* frame, const QString& msg);
bool javaScriptConfirm(QWebFrame* frame, const QString& msg);
QString userAgentForUrl(const QUrl& url) const;
QString mUserAgent;
QString mAlertString;
bool mPrintAlerts;
CutyCapt* mCutyCapt;
};
class CutyCapt : public QObject {
Q_OBJECT
public:
QString mUrl;
// TODO: This should really be elsewhere and be named differently
enum OutputFormat { SvgFormat, PdfFormat, PsFormat, InnerTextFormat, HtmlFormat,
RenderTreeFormat, PngFormat, JpegFormat, MngFormat, TiffFormat, GifFormat,
BmpFormat, PpmFormat, XbmFormat, XpmFormat, OtherFormat };
CutyCapt(CutyPage* page,
const QString& output,
int delay,
OutputFormat format,
const QString& scriptProp,
const QString& scriptCode);
private slots:
void HtmlDownloadFinished(QNetworkReply * reply);
void HtmlDownloadError(QNetworkReply::NetworkError code);
void DocumentComplete(bool ok);
void InitialLayoutCompleted();
void JavaScriptWindowObjectCleared();
void Timeout();
void Delayed();
private:
void TryDelayedRender();
void saveSnapshot();
bool mSawInitialLayout;
bool mSawDocumentComplete;
protected:
QString mOutput;
int mDelay;
CutyPage* mPage;
OutputFormat mFormat;
QObject* mScriptObj;
QString mScriptProp;
QString mScriptCode;
};
CutyCapt.cpp
////////////////////////////////////////////////////////////////////
//
// CutyCapt - A Qt WebKit Web Page Rendering Capture Utility
//
// Copyright (C) 2003-2010 Bjoern Hoehrmann <bjoern#hoehrmann.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// $Id: CutyCapt.cpp 7 2011-11-30 10:56:34Z hoehrmann $
//
////////////////////////////////////////////////////////////////////
#include <QApplication>
#include <QtWebKit>
#include <QtGui>
#include <QSvgGenerator>
#include <QPrinter>
#include <QTimer>
#include <QByteArray>
#include <QNetworkRequest>
#include <QtCore>
#include "CutyCapt.hpp"
#if QT_VERSION >= 0x040600 && 0
#define CUTYCAPT_SCRIPT 1
#endif
#ifdef STATIC_PLUGINS
Q_IMPORT_PLUGIN(qjpeg)
Q_IMPORT_PLUGIN(qgif)
Q_IMPORT_PLUGIN(qtiff)
Q_IMPORT_PLUGIN(qsvg)
Q_IMPORT_PLUGIN(qmng)
Q_IMPORT_PLUGIN(qico)
#endif
static struct _CutyExtMap {
CutyCapt::OutputFormat id;
const char* extension;
const char* identifier;
} const CutyExtMap[] = {
{ CutyCapt::SvgFormat, ".svg", "svg" },
{ CutyCapt::PdfFormat, ".pdf", "pdf" },
{ CutyCapt::PsFormat, ".ps", "ps" },
{ CutyCapt::InnerTextFormat, ".txt", "itext" },
{ CutyCapt::HtmlFormat, ".html", "html" },
{ CutyCapt::RenderTreeFormat, ".rtree", "rtree" },
{ CutyCapt::JpegFormat, ".jpeg", "jpeg" },
{ CutyCapt::PngFormat, ".png", "png" },
{ CutyCapt::MngFormat, ".mng", "mng" },
{ CutyCapt::TiffFormat, ".tiff", "tiff" },
{ CutyCapt::GifFormat, ".gif", "gif" },
{ CutyCapt::BmpFormat, ".bmp", "bmp" },
{ CutyCapt::PpmFormat, ".ppm", "ppm" },
{ CutyCapt::XbmFormat, ".xbm", "xbm" },
{ CutyCapt::XpmFormat, ".xpm", "xpm" },
{ CutyCapt::OtherFormat, "", "" }
};
QString
CutyPage::chooseFile(QWebFrame* /*frame*/, const QString& /*suggestedFile*/) {
return QString::null;
}
bool
CutyPage::javaScriptConfirm(QWebFrame* /*frame*/, const QString& /*msg*/) {
return true;
}
bool
CutyPage::javaScriptPrompt(QWebFrame* /*frame*/,
const QString& /*msg*/,
const QString& /*defaultValue*/,
QString* /*result*/) {
return true;
}
void
CutyPage::javaScriptConsoleMessage(const QString& /*message*/,
int /*lineNumber*/,
const QString& /*sourceID*/) {
// noop
}
void
CutyPage::javaScriptAlert(QWebFrame* /*frame*/, const QString& msg) {
if (mPrintAlerts)
qDebug() << "[alert]" << msg;
if (mAlertString == msg) {
QTimer::singleShot(10, mCutyCapt, SLOT(Delayed()));
}
}
QString
CutyPage::userAgentForUrl(const QUrl& url) const {
if (!mUserAgent.isNull())
return mUserAgent;
return QWebPage::userAgentForUrl(url);
}
void
CutyPage::setUserAgent(const QString& userAgent) {
mUserAgent = userAgent;
}
void
CutyPage::setAlertString(const QString& alertString) {
mAlertString = alertString;
}
QString
CutyPage::getAlertString() {
return mAlertString;
}
void
CutyPage::setCutyCapt(CutyCapt* cutyCapt) {
mCutyCapt = cutyCapt;
}
void
CutyPage::setPrintAlerts(bool printAlerts) {
mPrintAlerts = printAlerts;
}
void
CutyPage::setAttribute(QWebSettings::WebAttribute option,
const QString& value) {
if (value == "on")
settings()->setAttribute(option, true);
else if (value == "off")
settings()->setAttribute(option, false);
else
(void)0; // TODO: ...
}
// TODO: Consider merging some of main() and CutyCap
CutyCapt::CutyCapt(CutyPage* page, const QString& output, int delay, OutputFormat format,
const QString& scriptProp, const QString& scriptCode) {
mPage = page;
mOutput = output;
mDelay = delay;
mSawInitialLayout = false;
mSawDocumentComplete = false;
mFormat = format;
mScriptProp = scriptProp;
mScriptCode = scriptCode;
mScriptObj = new QObject();
// This is not really nice, but some restructuring work is
// needed anyway, so this should not be that bad for now.
mPage->setCutyCapt(this);
}
void
CutyCapt::InitialLayoutCompleted() {
mSawInitialLayout = true;
if (mSawInitialLayout && mSawDocumentComplete)
TryDelayedRender();
}
void
CutyCapt::DocumentComplete(bool /*ok*/) {
mSawDocumentComplete = true;
if (mSawInitialLayout && mSawDocumentComplete)
TryDelayedRender();
}
void
CutyCapt::JavaScriptWindowObjectCleared() {
if (!mScriptProp.isEmpty()) {
QVariant var = mPage->mainFrame()->evaluateJavaScript(mScriptProp);
QObject* obj = var.value<QObject*>();
if (obj == mScriptObj)
return;
mPage->mainFrame()->addToJavaScriptWindowObject(mScriptProp, mScriptObj);
}
mPage->mainFrame()->evaluateJavaScript(mScriptCode);
}
void CutyCapt::HtmlDownloadFinished(QNetworkReply * reply)
{
QVariant statusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
qDebug() << statusCode;
if ( !statusCode.isValid() )
{
QApplication::exit();
return;
}
int status = statusCode.toInt();
if( status == 301 || status == 302 )
{
QVariant loc = reply->header(QNetworkRequest::LocationHeader);
qDebug() << "Location :" << loc.toString();
QApplication::exit();
return;
}
if ( status != 200 )
{
QString reason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << reason;
QApplication::exit();
return;
}
QString html=QString::fromUtf8(reply->readAll());
mPage->mainFrame()->setHtml( html, QUrl(mUrl) );
}
void CutyCapt::HtmlDownloadError(QNetworkReply::NetworkError code)
{
QApplication::exit();
}
void
CutyCapt::TryDelayedRender() {
if (!mPage->getAlertString().isEmpty())
return;
if (mDelay > 0) {
QTimer::singleShot(mDelay, this, SLOT(Delayed()));
return;
}
saveSnapshot();
QApplication::exit();
}
void
CutyCapt::Timeout() {
saveSnapshot();
QApplication::exit();
}
void
CutyCapt::Delayed() {
saveSnapshot();
QApplication::exit();
}
void
CutyCapt::saveSnapshot() {
QWebFrame *mainFrame = mPage->mainFrame();
QPainter painter;
const char* format = NULL;
for (int ix = 0; CutyExtMap[ix].id != OtherFormat; ++ix)
if (CutyExtMap[ix].id == mFormat)
format = CutyExtMap[ix].identifier; //, break;
// TODO: sometimes contents/viewport can have size 0x0
// in which case saving them will fail. This is likely
// the result of the method being called too early. So
// far I've been unable to find a workaround, except
// using --delay with some substantial wait time. I've
// tried to resize multiple time, make a fake render,
// check for other events... This is primarily a problem
// under my Ubuntu virtual machine.
mPage->setViewportSize( mainFrame->contentsSize() );
switch (mFormat) {
case SvgFormat: {
QSvgGenerator svg;
svg.setFileName(mOutput);
svg.setSize(mPage->viewportSize());
painter.begin(&svg);
mainFrame->render(&painter);
painter.end();
break;
}
case PdfFormat:
case PsFormat: {
QPrinter printer;
printer.setPageSize(QPrinter::A4);
printer.setOutputFileName(mOutput);
// TODO: change quality here?
mainFrame->print(&printer);
break;
}
case RenderTreeFormat:
case InnerTextFormat:
case HtmlFormat: {
QFile file(mOutput);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream s(&file);
s.setCodec("utf-8");
s << (mFormat == RenderTreeFormat ? mainFrame->renderTreeDump() :
mFormat == InnerTextFormat ? mainFrame->toPlainText() :
mFormat == HtmlFormat ? mainFrame->toHtml() :
"bug");
break;
}
default: {
QImage image(mPage->viewportSize(), QImage::Format_ARGB32);
painter.begin(&image);
mainFrame->render(&painter);
painter.end();
// TODO: add quality
image.save(mOutput, format);
}
};
}
void
CaptHelp(void) {
printf("%s",
" -----------------------------------------------------------------------------\n"
" Usage: CutyCapt --url=http://www.example.org/ --out=localfile.png \n"
" -----------------------------------------------------------------------------\n"
" --help Print this help page and exit \n"
" --url=<url> The URL to capture (http:...|file:...|...) \n"
" --out=<path> The target file (.png|pdf|ps|svg|jpeg|...) \n"
" --out-format=<f> Like extension in --out, overrides heuristic \n"
// " --out-quality=<int> Output format quality from 1 to 100 \n"
" --min-width=<int> Minimal width for the image (default: 800) \n"
" --min-height=<int> Minimal height for the image (default: 600) \n"
" --max-wait=<ms> Don't wait more than (default: 90000, inf: 0)\n"
" --delay=<ms> After successful load, wait (default: 0) \n"
// " --user-styles=<url> Location of user style sheet (deprecated) \n"
" --user-style-path=<path> Location of user style sheet file, if any \n"
" --user-style-string=<css> User style rules specified as text \n"
" --header=<name>:<value> request header; repeatable; some can't be set\n"
" --method=<get|post|put> Specifies the request method (default: get) \n"
" --body-string=<string> Unencoded request body (default: none) \n"
" --body-base64=<base64> Base64-encoded request body (default: none) \n"
" --app-name=<name> appName used in User-Agent; default is none \n"
" --app-version=<version> appVers used in User-Agent; default is none \n"
" --user-agent=<string> Override the User-Agent header Qt would set \n"
" --javascript=<on|off> JavaScript execution (default: on) \n"
" --java=<on|off> Java execution (default: unknown) \n"
" --plugins=<on|off> Plugin execution (default: unknown) \n"
" --private-browsing=<on|off> Private browsing (default: unknown) \n"
" --auto-load-images=<on|off> Automatic image loading (default: on) \n"
" --js-can-open-windows=<on|off> Script can open windows? (default: unknown) \n"
" --js-can-access-clipboard=<on|off> Script clipboard privs (default: unknown)\n"
#if QT_VERSION >= 0x040500
" --print-backgrounds=<on|off> Backgrounds in PDF/PS output (default: off) \n"
" --zoom-factor=<float> Page zoom factor (default: no zooming) \n"
" --zoom-text-only=<on|off> Whether to zoom only the text (default: off) \n"
" --http-proxy=<url> Address for HTTP proxy server (default: none)\n"
#endif
#if CUTYCAPT_SCRIPT
" --inject-script=<path> JavaScript that will be injected into pages \n"
" --script-object=<string> Property to hold state for injected script \n"
" --expect-alert=<string> Try waiting for alert(string) before capture \n"
" --debug-print-alerts Prints out alert(...) strings for debugging. \n"
#endif
" -----------------------------------------------------------------------------\n"
" <f> is svg,ps,pdf,itext,html,rtree,png,jpeg,mng,tiff,gif,bmp,ppm,xbm,xpm \n"
" -----------------------------------------------------------------------------\n"
#if CUTYCAPT_SCRIPT
" The `inject-script` option can be used to inject script code into loaded web \n"
" pages. The code is called whenever the `javaScriptWindowObjectCleared` signal\n"
" is received. When `script-object` is set, an object under the specified name \n"
" will be available to the script to maintain state across page loads. When the\n"
" `expect-alert` option is specified, the shot will be taken when a script in- \n"
" vokes alert(string) with the string if that happens before `max-wait`. These \n"
" options effectively allow you to remote control the browser and the web page.\n"
" This an experimental and easily abused and misused feature. Use with caution.\n"
" -----------------------------------------------------------------------------\n"
#endif
" http://cutycapt.sf.net - (c) 2003-2010 Bjoern Hoehrmann - bjoern#hoehrmann.de\n"
"");
}
int
main(int argc, char *argv[]) {
int argHelp = 0;
int argDelay = 0;
int argSilent = 0;
int argMinWidth = 800;
int argMinHeight = 600;
int argMaxWait = 90000;
int argVerbosity = 0;
const char* argUrl = NULL;
const char* argUserStyle = NULL;
const char* argUserStylePath = NULL;
const char* argUserStyleString = NULL;
const char* argIconDbPath = NULL;
const char* argInjectScript = NULL;
const char* argScriptObject = NULL;
QString argOut;
CutyCapt::OutputFormat format = CutyCapt::OtherFormat;
QApplication app(argc, argv, true);
CutyPage page;
QNetworkAccessManager::Operation method =
QNetworkAccessManager::GetOperation;
QByteArray body;
QNetworkRequest req;
QNetworkAccessManager manager;
// Parse command line parameters
for (int ax = 1; ax < argc; ++ax) {
size_t nlen;
const char* s = argv[ax];
const char* value;
// boolean options
if (strcmp("--silent", s) == 0) {
argSilent = 1;
continue;
} else if (strcmp("--help", s) == 0) {
argHelp = 1;
break;
} else if (strcmp("--verbose", s) == 0) {
argVerbosity++;
continue;
#if CUTYCAPT_SCRIPT
} else if (strcmp("--debug-print-alerts", s) == 0) {
page.setPrintAlerts(true);
continue;
#endif
}
value = strchr(s, '=');
if (value == NULL) {
// TODO: error
argHelp = 1;
break;
}
nlen = value++ - s;
// --name=value options
if (strncmp("--url", s, nlen) == 0) {
argUrl = value;
} else if (strncmp("--min-width", s, nlen) == 0) {
// TODO: add error checking here?
argMinWidth = (unsigned int)atoi(value);
} else if (strncmp("--min-height", s, nlen) == 0) {
// TODO: add error checking here?
argMinHeight = (unsigned int)atoi(value);
} else if (strncmp("--delay", s, nlen) == 0) {
// TODO: see above
argDelay = (unsigned int)atoi(value);
} else if (strncmp("--max-wait", s, nlen) == 0) {
// TODO: see above
argMaxWait = (unsigned int)atoi(value);
} else if (strncmp("--out", s, nlen) == 0) {
argOut = value;
if (format == CutyCapt::OtherFormat)
for (int ix = 0; CutyExtMap[ix].id != CutyCapt::OtherFormat; ++ix)
if (argOut.endsWith(CutyExtMap[ix].extension))
format = CutyExtMap[ix].id; //, break;
} else if (strncmp("--user-styles", s, nlen) == 0) {
// This option is provided for backwards-compatibility only
argUserStyle = value;
} else if (strncmp("--user-style-path", s, nlen) == 0) {
argUserStylePath = value;
} else if (strncmp("--user-style-string", s, nlen) == 0) {
argUserStyleString = value;
} else if (strncmp("--icon-database-path", s, nlen) == 0) {
argIconDbPath = value;
} else if (strncmp("--auto-load-images", s, nlen) == 0) {
page.setAttribute(QWebSettings::AutoLoadImages, value);
} else if (strncmp("--javascript", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavascriptEnabled, value);
} else if (strncmp("--java", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavaEnabled, value);
} else if (strncmp("--plugins", s, nlen) == 0) {
page.setAttribute(QWebSettings::PluginsEnabled, value);
} else if (strncmp("--private-browsing", s, nlen) == 0) {
page.setAttribute(QWebSettings::PrivateBrowsingEnabled, value);
} else if (strncmp("--js-can-open-windows", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavascriptCanOpenWindows, value);
} else if (strncmp("--js-can-access-clipboard", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavascriptCanAccessClipboard, value);
} else if (strncmp("--developer-extras", s, nlen) == 0) {
page.setAttribute(QWebSettings::DeveloperExtrasEnabled, value);
} else if (strncmp("--links-included-in-focus-chain", s, nlen) == 0) {
page.setAttribute(QWebSettings::LinksIncludedInFocusChain, value);
#if QT_VERSION >= 0x040500
} else if (strncmp("--print-backgrounds", s, nlen) == 0) {
page.setAttribute(QWebSettings::PrintElementBackgrounds, value);
} else if (strncmp("--zoom-factor", s, nlen) == 0) {
page.mainFrame()->setZoomFactor(QString(value).toFloat());
} else if (strncmp("--zoom-text-only", s, nlen) == 0) {
page.setAttribute(QWebSettings::ZoomTextOnly, value);
} else if (strncmp("--http-proxy", s, nlen) == 0) {
QUrl p = QUrl::fromEncoded(value);
QNetworkProxy proxy = QNetworkProxy(QNetworkProxy::HttpProxy,
p.host(), p.port(80), p.userName(), p.password());
manager.setProxy(proxy);
page.setNetworkAccessManager(&manager);
#endif
#if CUTYCAPT_SCRIPT
} else if (strncmp("--inject-script", s, nlen) == 0) {
argInjectScript = value;
} else if (strncmp("--script-object", s, nlen) == 0) {
argScriptObject = value;
} else if (strncmp("--expect-alert", s, nlen) == 0) {
page.setAlertString(value);
#endif
} else if (strncmp("--app-name", s, nlen) == 0) {
app.setApplicationName(value);
} else if (strncmp("--app-version", s, nlen) == 0) {
app.setApplicationVersion(value);
} else if (strncmp("--body-base64", s, nlen) == 0) {
body = QByteArray::fromBase64(value);
} else if (strncmp("--body-string", s, nlen) == 0) {
body = QByteArray(value);
} else if (strncmp("--user-agent", s, nlen) == 0) {
page.setUserAgent(value);
} else if (strncmp("--out-format", s, nlen) == 0) {
for (int ix = 0; CutyExtMap[ix].id != CutyCapt::OtherFormat; ++ix)
if (strcmp(value, CutyExtMap[ix].identifier) == 0)
format = CutyExtMap[ix].id; //, break;
if (format == CutyCapt::OtherFormat) {
// TODO: error
argHelp = 1;
break;
}
} else if (strncmp("--header", s, nlen) == 0) {
const char* hv = strchr(value, ':');
if (hv == NULL) {
// TODO: error
argHelp = 1;
break;
}
req.setRawHeader(QByteArray(value, hv - value), hv + 1);
} else if (strncmp("--method", s, nlen) == 0) {
if (strcmp("value", "get") == 0)
method = QNetworkAccessManager::GetOperation;
else if (strcmp("value", "put") == 0)
method = QNetworkAccessManager::PutOperation;
else if (strcmp("value", "post") == 0)
method = QNetworkAccessManager::PostOperation;
else if (strcmp("value", "head") == 0)
method = QNetworkAccessManager::HeadOperation;
else
(void)0; // TODO: ...
} else {
// TODO: error
argHelp = 1;
}
}
if (argUrl == NULL || argOut == NULL || argHelp) {
CaptHelp();
return EXIT_FAILURE;
}
// This used to use QUrl(argUrl) but that escapes %hh sequences
// even though it should not, as URLs can assumed to be escaped.
req.setUrl( QUrl::fromEncoded(argUrl) );
QString scriptProp(argScriptObject);
QString scriptCode;
if (argInjectScript) {
QFile file(argInjectScript);
if (file.open(QIODevice::ReadOnly)) {
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
stream.setAutoDetectUnicode(true);
scriptCode = stream.readAll();
file.close();
}
}
CutyCapt main(&page, argOut, argDelay, format, scriptProp, scriptCode);
main.mUrl = argUrl;
app.connect(&page,
SIGNAL(loadFinished(bool)),
&main,
SLOT(DocumentComplete(bool)));
app.connect(page.mainFrame(),
SIGNAL(initialLayoutCompleted()),
&main,
SLOT(InitialLayoutCompleted()));
if (argMaxWait > 0) {
// TODO: Should this also register one for the application?
QTimer::singleShot(argMaxWait, &main, SLOT(Timeout()));
}
if (argUserStyle != NULL)
// TODO: does this need any syntax checking?
page.settings()->setUserStyleSheetUrl( QUrl::fromEncoded(argUserStyle) );
if (argUserStylePath != NULL) {
page.settings()->setUserStyleSheetUrl( QUrl::fromLocalFile(argUserStylePath) );
}
if (argUserStyleString != NULL) {
QUrl data("data:text/css;charset=utf-8;base64," +
QByteArray(argUserStyleString).toBase64());
page.settings()->setUserStyleSheetUrl( data );
}
if (argIconDbPath != NULL)
// TODO: does this need any syntax checking?
page.settings()->setIconDatabasePath(argUserStyle);
// The documentation does not say, but it seems the mainFrame
// will never change, so we can set this here. Otherwise we'd
// have to set this in snapshot and trigger an update, which
// is not currently possible (Qt 4.4.0) as far as I can tell.
page.mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
page.mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
page.setViewportSize( QSize(argMinWidth, argMinHeight) );
#if CUTYCAPT_SCRIPT
// javaScriptWindowObjectCleared does not get called on the
// initial load unless some JavaScript has been executed.
page.mainFrame()->evaluateJavaScript(QString(""));
app.connect(page.mainFrame(),
SIGNAL(javaScriptWindowObjectCleared()),
&main,
SLOT(JavaScriptWindowObjectCleared()));
#endif
app.connect( &manager,SIGNAL(finished(QNetworkReply *)), &main,SLOT(HtmlDownloadFinished(QNetworkReply *)));
QNetworkReply *reply = manager.get(req);
app.connect( reply,SIGNAL(error(QNetworkReply::NetworkError)), &main,SLOT(HtmlDownloadError(QNetworkReply::NetworkError)));
return app.exec();
}

QSignalSpy to capture a reference argument

It is not possible to capture an argument that has been passed as reference with a QSignalSpy:
QSignalSpy spy( myObject, SIGNAL(foo(int&)));
...
int& i=spy.at(0).at(0).value<int&>();
Since a QVariant can not contain a reference member. Plain logic.
But are there other solutions to check the passed-in argument?
Since Qt 5, we can simply connect to a lambda function, which makes the use of the QSignalSpy unnecessary:
std::vector<Value> values;
QObject::connect(myObject, &MyObject::foo,
[&](const auto &value)
{ values.emplace_back(value); });
myObject.somethingCausingFoo();
ASSERT_EQ(1u, values.size());
EXPECT_EQ(expectedValue, values.at(0));
An "ugly solution" would be to hack the fairly simple QSignalSpy code in order to handle the reference passed arguments. I provide a minimal working example for int reference arguments. The only changes were made to initArgs and appendArgs functions.
Notice that with this approach you will only be able to check the value of the passed argument by reference. You will not be able to change it's value.
In the initArgs function we check if we have references by argument and we populate the shouldreinterpret list.
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
// Check if we have a reference by removing the & from the parameter name
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
and the appendArgs function, where we reinterpret the passed by reference arguments:
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
list << QVariant(type, &(*reinterpret_cast<int*>(a[i + 1])));
break;
// Do the same for other types
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
Complete code for reference:
class MySignalSpy: public QObject, public QList<QList<QVariant> >
{
public:
MySignalSpy(QObject *obj, const char *aSignal)
{
#ifdef Q_CC_BOR
const int memberOffset = QObject::staticMetaObject.methodCount();
#else
static const int memberOffset = QObject::staticMetaObject.methodCount();
#endif
Q_ASSERT(obj);
Q_ASSERT(aSignal);
if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
return;
}
QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
const QMetaObject *mo = obj->metaObject();
int sigIndex = mo->indexOfMethod(ba.constData());
if (sigIndex < 0) {
qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
return;
}
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
Qt::DirectConnection, 0)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
sig = ba;
initArgs(mo->method(sigIndex));
}
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
int qt_metacall(QMetaObject::Call call, int methodId, void **a)
{
methodId = QObject::qt_metacall(call, methodId, a);
if (methodId < 0)
return methodId;
if (call == QMetaObject::InvokeMetaMethod) {
if (methodId == 0) {
appendArgs(a);
}
--methodId;
}
return methodId;
}
private:
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
int k = (*reinterpret_cast<int*>(a[i + 1]));
list << QVariant(type, &k);
break;
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
// the full, normalized signal name
QByteArray sig;
// holds the QMetaType types for the argument list of the signal
QList<int> args;
// Holds the indexes of the arguments that
QList<bool> shouldReinterpret;
};

Resources