I am converting some code from using QWidget to QOpenGLWidget, I want to use double buffering to render the widget, so far:
void clsElevStrip::initializeGL() {
qDebug() << "clsElevStrip::initializeGL()";
initializeOpenGLFunctions();
//Get the openGL context
mpobjContext = context();
if ( mpobjContext != NULL ) {
//Setup surface
mobjFormat.setDepthBufferSize(24);
mobjFormat.setSamples(4);
mobjFormat.setVersion(3, 0);
mobjFormat.setProfile(QSurfaceFormat::NoProfile);
mobjFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
int intSB = (int)mobjFormat.swapBehavior();
qDebug() << "swapBehavour as set-up in format: " << QString::number(intSB);
mpobjContext->setFormat(mobjFormat);
intSB = (int)mpobjContext->format().swapBehavior();
qDebug() << "swapBehavour as set-up in context: " << QString::number(intSB);
}
}
I can see in the debugger that intSB is 2 when checking swap behaviour from the format, but when I check intSB as set in the context it is 0, not 2?
As a result of this anything I render to the context is visible.
Resolved:
I modified the code, adding
mpobjContext->create();
After:
mpobjContext->setFormat(mobjFormat);
Now it works!
Related
I implement the ability to print reports in my project. Reports are presented as HTML content. There is an instance of QPrinter with custom fields:
printer = new QPrinter(QPrinter::ScreenResolution);
qreal topMargin = 15;
qreal bottomMargin = 15;
qreal leftMargin = 20;
qreal rightMargin = 15;
QPrinter::Unit units = QPrinter::Millimeter;
printer->setPageMargins(leftMargin,topMargin,rightMargin,bottomMargin,units);
When printing in PDF, everything is fine
view->printToPdf([=] (QByteArray bd) {
//Запись файла
}, printer->pageLayout());
But when printing with the "print" function, the fields are set incorrectly:
QWebEnginePage *page = new QWebEnginePage;
page->setHtml(currentForPrint);
connect(page, &QWebEnginePage::loadFinished, [page, printer] (bool ok) {
if (!ok) {
qDebug() << "error"; return;
}
page->print(printer, [=] (bool ok) {
if (ok)
qDebug() << "success";
else
qDebug() << "error 2";
});
});
image
Qt Version - 5.9.3.
This is a bug in WebEngine. I have reported it and will fix it as soon as it's possible. As a workaround you could enable full page printing in your QPrinter:
printer->setFullPage(true);
This works for me.
I'm working on a feature in my Qt program where you have the option to store the locations of your application's windows in a profile manner and load that profile later. After some research I found QSettings quite handy and useful for storing UI info like x, y etc. For detecting the windows I decided to use XLib.
I'm very beginner to using Xlib, I found this code which recursively query any open window starting from ":0.0". Inside the foreach loop I did this save function:
int x, y;
int xGeo, yGeo;
int width, height, border, depth;
Display *disp = XOpenDisplay(":0.0");
Window rootWin = XDefaultRootWindow(disp);
QList<Window> windows = listXWindowsRecursive(disp, rootWin);
char* name = NULL;
XWindowAttributes* attributes;
foreach(Window win, windows)
{
attributes = new XWindowAttributes;
// Enumerate through all windows
if(XFetchName(disp, win, &name) && name != NULL)
{
if(strcmp(name, "Secondary Window") == 0)
{
Window child;
qDebug() << (name);
XTranslateCoordinates(disp, win, rootWin, 0, 0, &x, &y, &child);
qDebug() << "XC = " << x << " - " << "YC = " << y;
if(XGetGeometry(disp, win, &rootWin, &xGeo, &yGeo, &width, &height, &border, &depth))
qDebug() << "XGeo= " << x - xGeo << " - " << "YGeo= " << y - yGeo;
settings.setValue("secondaryWindow/x", x - xGeo);
settings.setValue("secondaryWindow/y", y - yGeo);
settings.setValue("secondaryWindow/width", width);
settings.setValue("secondaryWindow/height", height);
if(XGetWindowAttributes(disp, win, attributes))
{
qDebug() << "X = " << x - attributes->x << " - " << "Y = " << y - attributes->y;
}
}
}
}
This portion gets the Qt UI info like X and Y and stores them using QSettings
First thing I'm not sure if what I wrote is the right way to get the X and Y for the window because of these facts. Also I'm not sure if I'm correctly passing the required parameters.
Second thing, this is the load function:
if(settings.contains("secondaryWindow/x") && settings.contains("secondaryWindow/y"))
{
SecondaryWindow* win = new SecondaryWindow;
win->setGeometry(settings.value("secondaryWindow/x").toInt(),
settings.value("secondaryWindow/y").toInt(),
settings.value("secondaryWindow/width").toInt(),
settings.value("secondaryWindow/height").toInt());
win->show();
}
Here is the weird scenario, there is a button that opens the secondary window (using new and show) which works fine. Back to the main window clicking the save function, I get the correct secondary window info.
Now If I closed the secondary window and then clicked the load function, the secondary window opens correctly in the previous position BUT, clicking the save function again here will tell me that there are two "Secondary Window" instances but the second one has X and Y = 0.
By "closing the window" I mean using either the "X" button and using the close() slot. Could it be that the window is not closed correctly? I already implemented the closeEvent handler:
event->accept();
QWidget::closeEvent(event);
Any help is appreciated, Thanks!
I want to be able to apply tranformations to Polydata but no matter how I try to do it, it just doesn't work.
Here is what I have for "drawing" my polydata in a class call Drawing.cpp:
Drawing.h
vtkSmartPointer<vtkPlane> clipPlane;
vtkSmartPointer<vtkImplicitPlaneRepresentation> planeRep;
vtkSmartPointer<vtkActor> actorPlaneSource;
vtkSmartPointer<vtkActor> mainActor;
vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter;
vtkSmartPointer<vtkTransform> translation ;
vtkContextView* ctxView ;
vtkRenderWindow* win ;
vtkRenderer* ren ;
vtkCamera* cam ;
vtkSmartPointer<vtkPolyData> inputPolyData;
Then the read function is called and starts the rendering, here is the function in drawing.cpp:
void Drawing::read(){
std::string filename = BUNNY;
// Read all the data from the file
vtkSmartPointer<vtkXMLPolyDataReader> reader =vtkSmartPointer<vtkXMLPolyDataReader>::New();
reader->SetFileName(filename.c_str());
reader->Update();
inputPolyData = reader->GetOutput();
cout << "File Found and Loaded : " << filename << endl ;
vtkSmartPointer<vtkTransform> translation = vtkSmartPointer<vtkTransform>::New();
translation->Translate(0.3, -0.05, 0);
transformFilter = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
//transformFilter->SetInputConnection(reader->GetOutputPort());
transformFilter->SetInputData(inputPolyData);
transformFilter->SetTransform(translation);
//transformFilter->Update();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(transformFilter->GetOutputPort());
mainActor = vtkSmartPointer<vtkActor>::New();
mainActor->SetMapper(mapper);
ren->AddActor(mainActor);
vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
iren->SetRenderWindow(win);
vtkInteractorStyleMultiTouchCamera *style =
vtkInteractorStyleMultiTouchCamera::New();
iren->SetInteractorStyle(style);
//Start the event loop
iren->Initialize();
iren->Start();
defineClipping();
win->PolygonSmoothingOn();
win->Render();
win->Start();
}
From this, I have an other thread running a server that gets messages and has a pointer to my drawing object and is supposed to call one of these three functions depending on the message it gets from its clients:
void Drawing::scale(float k){
vtkSmartPointer<vtkTransform> transform =vtkSmartPointer<vtkTransform>::New();
transform->Scale(5,1,1);
vtkSmartPointer<vtkTransformFilter> transformFilter = vtkSmartPointer<vtkTransformFilter>::New();
transformFilter->SetInputConnection(cone->GetOutputPort());
transformFilter->SetTransform(transform);
mapper->SetInputConnection(transformFilter->GetOutputPort());
ren->GetActiveCamera();
}
void Drawing::translate(float x, float y, float z){
cout << "Translate: " << x << " - " << " - " << y << " - " << z << endl ;
vtkSmartPointer<vtkTransform> transform1a = vtkSmartPointer<vtkTransform>::New();
//transform1a->Translate(x,y,z);
//transformFilter->SetTransform(transform1a);
//transformFilter->Update();
double* position = mainActor->GetPosition();
mainActor->SetPosition(position[0]+x,position[1]+y,position[2]+z);
}
void Drawing::rotate(float x, float y, float z){
cout << "Rotate: " << x << " - " << " - " << y << " - " << z << endl ;
vtkSmartPointer<vtkTransform> transform1a = vtkSmartPointer<vtkTransform>::New();
//transform1a->PostMultiply();
//transform1a->RotateX(x);
//transform1a->RotateY(y);
//transform1a->RotateZ(z);
//mainActor->SetUserTransform(transform1a);
mainActor->RotateWXYZ(20,1,0,0);
}
None of these functions work because nothing is changing in the rendering windows unless I click in the rendering window itself.
So I thought maybe I should try and add to every transformation functions: ctxView->Render();
But when I do I get:
Error the ressource is already busy.
I'm a newbie in VTK but I find it weird that I can't even do a simple transformation to an object. Would really like to get some help with that.
EDIT:
Ok so after hours of trying different things I have notices that if I comment out the line iren->Start(); my rotations and translations are called. However, the program closes as soon as there done and I cannot interact with my window anymore. Would you have some insights on that?
Thanks in advance.
Yes, I could try to give a little code that made rotations to a polydata, but I guess that is something different from your pipeline (I am using vtkImageReslice object as m_pReslice):
int nExtent[3];
double dSpacing[3];
double dOrigin[3];
m_pReader->GetOutput()->GetSpacing(dSpacing);
m_pReader->GetOutput()->GetOrigin(dOrigin);
m_pReader->GetOutput()->GetDimensions(nExtent);
double dCenter[3];
dCenter[0] = dOrigin[0] + dSpacing[0] * 0.5 * nExtent[0]; // nExtent[0] is width
dCenter[1] = dOrigin[1] + dSpacing[1] * 0.5 * nExtent[1]; // nExtent[1] is height
dCenter[2] = dOrigin[2] + dSpacing[2] * 0.5 * nExtent[2]; // nExtent[2] is depth
vtkSmartPointer<vtkTransform> pTransform = vtkSmartPointer<vtkTransform>::New();
pTransform->PreMultiply();
int nDirection = CDirectionDlg::GetDirection();
if(CDirectionDlg::DIR_AXIAL == nDirection)
{
pTransform->Translate(dCenter[0], 0, dCenter[2]);
pTransform->RotateY(180);
pTransform->Translate(-dCenter[0], 0, -dCenter[2]);
}
else
{
pTransform->Translate(dCenter[0], dCenter[1], 0);
pTransform->RotateZ(180);
pTransform->Translate(-dCenter[0], -dCenter[1], 0);
}
m_pReslice->SetResliceTransform(pTransform);
m_pReslice->SetInterpolationModeToLinear();
m_pReslice->Update();
Consider that another way to rotate an object in a view is to moving camera ... I hope it help you.
As flaviu2 wrote, you absolutely need to call
ren->Render();
after updating your vtkTransform. Renderers do not watch VTK objects that are being rendered to see if anything has been updated. You need to call the Render() member function explicitly.
Beware threading. It is possible to use threading, but most of VTK is not thread safe, and it is probably going to cause you some headaches. To separate this problem from potential problems caused by using different threads to update objects, I would try to get this working without threading, and update this question if you still encounter problems.
Since your problem is the interactor, try doing this hack. Waht it does is create a timer for the interactor that will help get out of the start blocking method :
class CommandSubclass2 : public vtkCommand
{
public:
vtkTypeMacro(CommandSubclass2, vtkCommand);
static CommandSubclass2 *New()
{
return new CommandSubclass2;
}
void Execute(vtkObject *vtkNotUsed(caller), unsigned long vtkNotUsed(eventId),
void *vtkNotUsed(callData))
{
std::cout << "timer callback" << std::endl;
renderWindowInteractor->ExitCallback();
}
};
// in your main
vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
iren->SetRenderWindow(win);
iren->CreateRepeatingTimer(1);
vtkSmartPointer<CommandSubclass2> timerCallback =
vtkSmartPointer<CommandSubclass2>::New();
iren->AddObserver ( vtkCommand::TimerEvent, timerCallback );
vtkInteractorStyleMultiTouchCamera *style =
vtkInteractorStyleMultiTouchCamera::New();
iren->SetInteractorStyle(style);
while(true)
{
iren->Start();
ren->Render();
}
Probably not the good way of doing it, but I don't know any other way to get out of the interactor blocking method start()
So thanks to the help of people here and some research with the code and documentation I've figured that this part in my code was responsible for nothing happening when trying to rotate/translate/scale my Polydata:
//Start the event loop
iren->Initialize();
iren->Start();
Indeed the start() method of the vtkRenderWindowInteractor is blocking and therefore no matter what I could call afterward was not processed. Getting rid of it allows me to call my transformation functions. Yet with the loss of the possible interaction with the data and the window closing right after every transformation is applied.
In a QTextEdit object, let's say I want to know the character's position under the mouse cursor.
I can write...
void MyQTextEditObject::mousePressEvent(QMouseEvent* mouse_event) {
mycursor = this->textCursor();
qDebug() << "pos=" << mycursor.position();
}
... it works (the mouse position changes from 0 to the last index of the last character) but the mousePressEvent() method creates a new cursor every time an event occurs. It bothers me since I don't know the "cost" of such a creation.
So, why not create a cursor attribute and use it in mousePressEvent() ?
Something like :
class MyQTextEditObject : public QTextEdit {
Q_OBJECT
public:
// [...]
QTextCursor cursor;
}
MyQTextEditObject::MyQTextEditObject(QWidget* parent) : QTextEdit(parent) {
// [...]
this->cursor = this->textCursor();
}
void MyQTextEditObject::mousePressEvent(QMouseEvent* mouse_event) {
qDebug() << "pos=" << this->cursor.position();
}
But the position doesn't change anymore, as if it was fixed. So, is there a way to somehow update the cursor ? Or is the cost of repeated creation of a QTextCursor insignificant ?
update : writing something like...
mycursor= this->cursorForPosition(mouse_event->pos());
... creates a new cursor and seems to be the equivalent to :
mycursor= this->textCursor();
In your first example, instead of
mycursor = this->textCursor();
qDebug() << "pos=" << mycursor.position();
why do not you call it directly as?
qDebug() << "pos=" << this->textCursor().position();
Because in python
self.textCursor().position()
works.
Also, I am not sure but in your second example maybe you need to set the "cursor" as "textCursor" again with setTextCursor().
void MyQTextEditObject::mousePressEvent(QMouseEvent* mouse_event) {
this->setTextCursor(cursor)
qDebug() << "pos=" << this->cursor.position();
}
I have a QTreeWidget in which each of its items has a QComboBox in a column. I have connected it to a slot with a QSignalMapper, and I'm successfully retrieving both the item and the index in the combobox when it is triggered. I did it like this:
foreach(Workplace *wp, allWorkplaces){
QTreeWidgetItem *workplaceItem = new QTreeWidgetItem;
workplaceItem->setText(0, wp->workplaceName());
workplaceItem->setText(1, wp->workplaceDescription());
myWorkplaceUi->treeWidget->addTopLevelItem(workplaceItem);
QComboBox *combo = new QComboBox();
combo->addItems(allShiftModels);
combo->setAutoFillBackground(true);
ShiftModel *shiftModel = qobject_cast<ShiftModel *>(wp->usedShiftModel);
myWorkplaceUi->treeWidget->setItemWidget(workplaceItem,2, combo);
if(shiftModel && !shiftModel->shiftModelName().isEmpty()){
qDebug()<<"after the cast: "<< shiftModel->shiftModelName();
combo->setCurrentIndex(combo->findText(shiftModel->shiftModelName(), Qt::MatchExactly));
}else{
combo->setCurrentIndex(combo->findText("None", Qt::MatchExactly));
}
connect(combo, SIGNAL(currentIndexChanged(int)), signalMapper, SLOT(map()));
signalMapper->setMapping(combo, QString("%1").arg(wp->workplaceName()));
}
connect(signalMapper, SIGNAL(mapped(const QString &)),this, SLOT(changed(const QString &)));
My objective is, after retrieving both the Workplace and the ShiftModel, to update them in the instances of my already created Workplaces. So, basically, I try to find the Workplace and the ShiftModel that were selected, because depending on the ShiftModel selected, I will change a pointer to the ShiftModel in the Workplace class:
class Workplace : public QObject
{
Q_OBJECT
public:
(...)
ShiftModel *usedShiftModel;
(...)
}
And the changed slot:
void workplacesdialog::changed(QString position){
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
QList<ShiftModel*> allShiftModels = this->myProject->myFactory->listShiftModels();
foreach(Workplace* workplace, allWorkplaces){
foreach(ShiftModel *shiftmodel, allShiftModels){
qDebug() <<"workplace:"<< workplace->workplaceName();
qDebug() <<"shiftmodel:"<< shiftmodel->shiftModelName();
QString wp = position;
QTreeWidgetItem* item=(QTreeWidgetItem*)myWorkplaceUi->treeWidget->findItems(wp,Qt::MatchExactly,0).at(0);
QComboBox *combo = (QComboBox*)myWorkplaceUi->treeWidget->itemWidget(item,2);
if(combo && item){
QString sm = combo->currentText();
qDebug() << "selected shiftmodel "<< sm << " on workplace "<< wp;
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
workplace->usedShiftModel = shiftmodel;
break;
}
else{
workplace->usedShiftModel = 0;
return;
}
}else{
qDebug() << "cast failed!";
return;
}
}
}
}
So, my problem with this is, when I click one of the comboboxes, successfully retrieve both the item and index selected, but then, when I try to traverse them with the two foreach loops in the slot, it does not work as I expected. I hoped that every time I clicked on an index in one of the comboboxes, this would be called, and it is. Although, for some reason, the method I'm using to match what the user selected with what was already instatiated doesn't work.
Also, it looks like it only hits both the 1st workplace on the allWorkplaces list as well as the 1st shiftmodel on the ShiftModels list, and this is my problem.
If anyone knows how to fix this or has any ideas to share, please let me know. Thank you.
The problem is this:
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
workplace->usedShiftModel = shiftmodel;
break;
}
else{
workplace->usedShiftModel = 0;
return;
}
If either the workplace name does not match, or the shift model name does not match, the relation between the work place and its currently linked shift model is removed and your function returns.
I could restructure the two for-loops for you, but there is an easier and less error-prone way:
Note: I marked some code paths with "TODO" which I skipped due to lack of time. You should be able to figure them out yourself though.
// Set up hashes for quick lookup
QHash< QString, Workplace* > workplaceHash;
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
foreach( Workplace* workplace, allWorkplaces )
{
workplaceHash.insert( workplace, workplace->workplaceName() );
}
// TODO: Do a similar thing for the shift models here
// Find the selected workplace
if( !workplaceHash.contains( position ) )
{
// TODO: Error handling (An unknown/No workplace was selected)
return;
}
// else: A valid workplace was selected
Workplace* selectedWorkplace = workplaceHash.value( position );
// TODO: Retrieve the name of the shift model (stored in variable sm)
// Find the selected shiftmodel
if( !shiftplaceHash.contains( sm ) )
{
// No shift model was selected
selectedWorkplace->usedShiftModel= 0;
return;
}
// Else: Both work place and shift model were selected
Shiftplace* selectedShiftModel = shiftplaceHash.value( sm );
selectedWorkplace->usedShiftModel = selectedShiftModel;
A few ideas for refactoring:
You could maybe do the creation of the hashes outside of this method and store them in member variables. Just make sure that the hash is always updated when a workplace is added or removed.
You could make spotting errors easier by extracting parts of the code into separate methods, e.g. QString getSelectedShiftModelName() etc.
So, In the end I figured out my loops were really messed up... This is working now:
void workplacesdialog::changed(QString position){
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
QList<ShiftModel*> allShiftModels = this->myProject->myFactory->listShiftModels();
qDebug() << allWorkplaces.size() << " workplaces";
qDebug() << allShiftModels.size() << " ShiftModels";
QString wp = position;
QString sm;
QTreeWidgetItem* item=(QTreeWidgetItem*)myWorkplaceUi->treeWidget->findItems(wp,Qt::MatchExactly,0).at(0);
QComboBox *combo = (QComboBox*)myWorkplaceUi->treeWidget->itemWidget(item,2);
if(combo && item){
sm = combo->currentText();
qDebug() << "selected shiftmodel "<< sm << " on workplace "<< wp;
}else{
qDebug() << "cast failed!";
return;
}
foreach(Workplace* workplace, allWorkplaces){
foreach(ShiftModel *shiftmodel, allShiftModels){
qDebug() <<"workplace:"<< workplace->workplaceName();
qDebug() <<"shiftmodel:"<< shiftmodel->shiftModelName();
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
qDebug() << "found match!: "<< wp << " >>>>> " << sm;
workplace->usedShiftModel = shiftmodel;
return;
}else if(workplace->workplaceName()==wp && sm=="None"){
qDebug() << "clear match: "<< wp << " >>>>> " << sm;
workplace->usedShiftModel = 0;
return;
}
}
}
}