I have a few keys that can be pressed, and each key press emits a signal that corresponds to it. However, the key presses do different things depending on which object is being operated on. I have an example illustration below.
// keyboard.cpp
void receive_key_press(uint8_t key_id){
if(key_id == 1)
emit key_one_pressed();
else if(key_id == 2)
emit key_two_pressed();
else if(key_id == 3)
emit key_three_pressed();
}
Class A : public QObject {
// use Q_PROPERTY here to expose the member var 'model' to qml
public:
int model;
}
int main(){
Keyboard keyboard;
A obj;
if (some_condition)
obj.model = 5;
else
obj.model = 7;
// Engine initialization here and exposing 'obj' and 'keyboard' to qml using setContextProperty
}
Then in main.qml
Connections {
target: keyboard
onKey_one_pressed: {
if (obj.model == 5)
// do something
else
// do something else
// execute some common code here
}
onKey_two_pressed: {
if (obj.model == 5)
// do something
else
// do something else
}
onKey_three_pressed: {
if (obj.model == 5)
// do something
else
// do something else
}
}
How can I simplify and avoid all those if-else conditions in the main.qml. Is there something in Qt that can make this easier, and provide some global way of handling a 'Gui state'. I know there's like a File Selector in Qt but that's not really what I'm going for. I want to have a better approach instead of having if-else everywhere in the Gui for different model.
Thanks in advance
In your particular example, factor it out into a common function:
Connections {
target: keyboard
function commonCode() {
// execute some common code here
}
onKey_one_pressed: {
if (obj.model == 5)
// do something
else
// do something else
commonCode();
}
onKey_two_pressed: {
if (obj.model == 5)
// do something
else
// do something else
commonCode();
}
onKey_three_pressed: {
if (obj.model == 5)
// do something
else
// do something else
commonCode();
}
}
You can also put commonCode in a more broadly accessible place to further reduce code duplication.
However, there is also QML States: https://doc.qt.io/qt-5/qtquick-statesanimations-states.html
This is a very handy way to manage different configurations of your GUI. You can use PropertyChanges with it to override the signal handler of objects based on your model value.
Related
I am trying to make a program where you are allowed to select between an option of shapes, and then drawing it. To allow for multiple shapes I created a vector of a class which creates shapes (Shapes are set up with the chosen function). My problem is the mouse click is too long, so it assigns it to everything in the vector, so you can't create a new shape. Is there a problem in my logic, or is there a problem in the code?
Here is my attempt:
for (auto& it : onCanvas) {
if (Mouse::isButtonPressed(Mouse::Left)) {
if (mousepointer.getGlobalBounds().intersects(circleOption.getGlobalBounds())) {
it.chosen(circles);
}
if (mousepointer.getGlobalBounds().intersects(rectOption.getGlobalBounds())) {
it.chosen(rectangle);
}
if (mousepointer.getGlobalBounds().intersects(triOption.getGlobalBounds())) {
it.chosen(triangles);
}
if (mousepointer.getGlobalBounds().intersects(it.shape.getGlobalBounds()) || it.dragging) {
it.shape.setPosition(mousepointer.getPosition());
it.dragging = true;
}
}
if (!Mouse::isButtonPressed) {
it.dragging = false;
}
win.draw(it.shape);
}
Your source-code is a bit incomplete (what is onCanvas and mousepointer). But I guess the problem is that this snippet is called multiple times while your mouse is clicked. To avoid that you can do two thing.
In the first solution you use events, so you only add shapes when the state of the mousebutton changes (you can additionally listen to the MouseButtonReleased to simulate a full click):
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.mouseButton.button == sf::Mouse::Left)
{
// Hit Detection
}
}
or second solution you remember the last state of the button (probably do the mouse check once outside of the for loop):
bool mouse_was_up = true;
if (mouse_was_up && Mouse::isButtonPressed(Mouse::Left)) {
mouse_was_up = false;
for (auto& it : onCanvas) {
// Hit Detection
}
}
else if (!Mouse::isButtonPressed(Mouse::Left))
mouse_was_up = true;
I would rather stick to the first solution because when your click is too short and your gameloop is in another part of the game logic, you can miss the click.
My question is:
I am trying to implement basic state management in my project and i stuck at changing states.
I have all my states in std::stack<State*> container, and push/pop them directly from Application class or from State class.
Problem is when i change current state from State class, it can be destroyed before render method called, whitch results in exeption. So how do i avoid this?
PS sorry for my english and please say me if something in my problem/code isn clear
Application class:
void Application::pushState(State* state)
{
this->m_states.push(state);
this->m_states.top()->open();//enter state
}
void Application::popState()
{
if (!this->m_states.empty())
{
this->m_states.top()->close();//leave state
delete this->m_states.top();
}
if (!this->m_states.empty())
this->m_states.pop();
}
void Application::changeState(State* state)
{
if (!this->m_states.empty())
popState();
pushState(state);
}
State* Application::peekState()
{
if (this->m_states.empty()) return nullptr;
return this->m_states.top();
}
void Application::mainLoop()
{
sf::Clock clock;
while (this->m_window.isOpen())
{
sf::Time elapsed = clock.restart();
float delta = elapsed.asSeconds();
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->update(delta)//if i change state in State.update(), it may be that code below will now point to not existing state
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->render(delta);
}
}
State class:
void EditorState::update(const float delta)
{
sf::Event event;
while (this->m_application->m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
this->m_application->popState();
return;
}
}
}
Okay maybe this is not really a problem, but something like "how to" question. As you can see in my code, i update and render states in mainLoop() method. What im tying to figure out is how to manage those updates, asuming that state can be changed from state itself, not only from stateManager (in my case Application class)
Ok, so I'm guessing this is for a game (but it doesn't have to be). Instead of doing what you're doing for switching between states, I use an enum.
enum class GameState {
MENU, PLAY, PAUSE
}
Then, in your main header
GameState m_gameState = GameState::MENU;
In your main loop, you can check what the current state is by simply doing
if (m_gameState == GameState::MENU)
{
...
}
or you can use a switch statement
switch (m_gameState)
{
case GameState::MENU:
...
break;
case GameState::PLAY:
...
break;
case GameState::PAUSE:
...
break;
}
And, if you ever want to switch the state, you can just do
m_gameState = GameState::PAUSE;
Hope this answered your question :D
If not, I must have misunderstood (sorry).
I have wrote an application in Qt/c++ on OSX. When quitting the app, I'm catching the closeevent to display dialog box
void MainUI::closeEvent (QCloseEvent *event)
{
if( DeviceUnplugged == false) {
ExitDialog = new DialogExit;
ExitDialog->exec();
if(ExitDialog->result() == QDialog::Accepted) {
m_device.CloseDevice();
event->accept();
}
else {
event->ignore();
}
}
}
The dialog box is correctly displayed when closing using the red cross or using the menu "quit".
but when I'm closing the app using the right click on the icon in the dock, the dialog box appears twice the close event is called twice.
Any idea why ?
Yes, I think it is normal for Mac, at least I had this in my Qt application, too (only on Mac).
I used the following workaround:
void MainUI::closeEvent (QCloseEvent *event)
{
if (m_closing)
{
event->accept();
return;
}
if( DeviceUnplugged == false) {
ExitDialog = new DialogExit;
ExitDialog->exec();
if(ExitDialog->result() == QDialog::Accepted) {
m_device.CloseDevice();
m_closing = true;
event->accept();
}
else {
event->ignore();
}
}
}
By default, boolean variable m_closing should be initialized by false of course in your class. This way second time nothing will be done (processing will be skipped). This worked for me.
Looks like this is a QT bug:
See: https://bugreports.qt.io/browse/QTBUG-43344
Also had this problem when using qt-5.6_4 ,
In my case it happened when using CMD+Q but didn't happen when using the red x button.
Used a similar patch.
I avoided accept or ignore since this is a bug and I don't think we should "talk to it" :-)
Instead I simply return when called more then once.
static int numCalled = 0;
if (numCalled++ >= 1)
return;
I'm new to VTK.
I was reading VTK sources and in Rendering/vtkInteractorStyleTrackballActor.cxx I found this method:
void vtkInteractorStyleTrackballActor::OnLeftButtonDown()
{
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
this->FindPokedRenderer(x, y);
this->FindPickedActor(x, y);
if (this->CurrentRenderer == NULL || this->InteractionProp == NULL)
{
return;
}
this->GrabFocus(this->EventCallbackCommand);
if (this->Interactor->GetShiftKey())
{
this->StartPan();
}
else if (this->Interactor->GetControlKey())
{
this->StartSpin();
}
else
{
this->StartRotate();
}
}
Can you explain what call
this->GrabFocus(this->EventCallbackCommand);
actually does?
In docs I found
void vtkInteractorObserver::GrabFocus(vtkCommand *mouseEvents,
vtkCommand *keypressEvents = NULL)
These methods enable an interactor observer to exclusively grab all events invoked by its associated vtkRenderWindowInteractor. (This method is typically used by widgets to grab events once an event sequence begins.) The GrabFocus() signature takes up to two vtkCommands corresponding to mouse events and keypress events. (These two commands are separated so that the widget can listen for its activation keypress, as well as listening for DeleteEvents, without actually having to process mouse events.)
But I don't really understand what they mean. Please give me some simple examples how this method is typically used.
I have a set of codes in my function with a lot of if-else loops but each doing a diffrent one
like
if(ddlname.SelectedIndex = 0)
{
//do this
}
else
{
//do this
}
if (txtprice.Text ="")
{
//do tis
}
else
{
//do this
}
my whole program looks clumsy and unnecessarily long because of this one. I have some 20 dropdownlists and ten textboxes. Is there way to make this as simple as 1 or 2 for loops ?
I am currently reading Clean Code by Robert C. Martin. According to his book, you should refactor your method into several smaller methods, doing exactly one thing. You should for example extract every do this into its own method.
As to your question, I don't think there is any way of achieving the same logic using for loops, unless you do the same for every call.
foreach (ctl in page.ctls)
{
TextBox tempTextBox = ctl as TextBox;
if (tempTextBox != null)
{
doTheSameForEveryTextBox(tempTextBox)
}
DropDownList tempDropDownList as DropDownList; // not sure if this is the right Type...
if (tempDropDownList != null)
{
doTheSameForEveryTextBox(tempDropDownList)
}
}
void doTheSameForEveryTextBox(TextBox tempTextBox)
{
if (tempTextBox.Text == "")
{
//TODO: implement your code here
}
}
void doTheSameForEveryDropDownList(DropDownList tempDropDownList)
{
if (tempDropDownList.SelectedIndex == 0)
{
//TODO: implement your code here
}
}