 Hey everyone, this is Brian, and in this video we are going to make a complete application using all the knowledge we've gained in this series so far. Now this application is going to be a very simple, I have to stress the term simple, inventory application. At its core it's going to be a QMAP that we're going to save to a file and load back. We're using a QMAP because we want a key value pair like an item number or an item name with a quantity attached to it. And of course we're going to have some extra code in there so we can add or remove items from that inventory. I'm going to do this in this series from time to time because our YouTube series is, well I like it but each video is very self-contained, meaning one video covers say files, the next video covers say Q-list, they're very self-contained and these specific videos where we build an application takes all these parts and kind of puts them all into one so you see how all the pieces fit together to make a complete app. So I am going to do this from time to time. I haven't really decided on like X number of videos so it's not going to be like every tenth video or anything like that but drop a comment below, let me know how often you want to see these. Now because we're still in newbie land in this series this is going to be a command line application but yes as this series progresses we're going to do QML and widget applications as well. Now if you haven't watched these videos and you're a complete newbie when it comes to Qt I suggest you watch these and if you want more classroom style training I do offer complete classes out on Udemy specifically. This topic would be touched on in the Qt6 core for beginners with C++ and the Qt6 core intermediate with C++. This video also is going to hyper focus on Qt data stream which is how we put streams of data into an IO device specifically a file. Let's dive in and take a look. Okay let's start at the very beginning I don't even have Qt Creator open I'm going to walk through this whole thing step by step so this is Linux Mint but again Qt runs anywhere so you may be on Windows Mac whatever this code will run just perfectly fine on all of those platforms. So we're going to go to programming we're going to open up Qt Creator and then from here I'm going to say new and we're going to choose console application. Now I need to give it a name name this whatever you want but this is going to be the name of your executable for my purposes because I want this to follow a specific standard out on GitHub that's right the code for this will be out on GitHub I'm going to say Qt6 and we're going to say episode and this is episode 20 put it in whatever folder you want and hit next we're going to use the CMake build system I'm not going to have a translation file kit selection I only have Qt6 in here if you have an older version like Qt5 you may run into some little issues some little differences between five and six remember this series is covering Qt6. It next not going to use any project management and here we go we just have a blank application now I'm going to go ahead and I'm going to right click our project and I'm going to add new and I want to make a class and let's go ahead and call this inventory this is really a design decision and unfortunately when it comes to design decisions there's a million different ways you can do this I'm putting my business logic into a class and I'm going to make this a Q object that way I can call it via signals and slots if I decide to make this application more complex in the future need to make sure this includes Q object and add the Q underscore object macro that officially makes it a Q object we have our header and our source hit next and then just hit finish this little guy is annoying Q if you're watching please pretty please make this go into CMakeList.txt automatically but for now inventory see gotta type this out hit save make sure it adds the header and the source file and we are good to go now we've got our class here but it's well just a class there's really nothing in here it's just a blank class we need to flesh this code out and I'm going to break this up in a specific sections but this section right here we're going to talk about the class design so the first thing I want is some includes and from time to time I do copy and paste I have some notes off the screen just to save a smidge of time typing we're going to use Q map Q debug so we can see some output on the screen Q files so we can work with you guessed it a file and then Q data stream this is something we haven't really touched on in this series but did cover in the Udemy courses so what is Q data stream if you highlight it hit F1 you can see the integrated help it's a class that provides serialization of binary data to an IO device an IO device of course would be a file yes you could also do this to like a socket or something like that it's really just that cool once you get more advanced but at its core a data stream will serialize the data meaning it's going to take it and turn it into a language or code that the machine understands but this is not human readable it's much more efficient to store and to retrieve that's why we're going to serialize it if you want more help just highlight Q data stream hit F1 and read down through the file but we're going to cover some of the basics of this class and there's a lot to it so I stress the basics from here we need to make a couple other design decisions as well I'm going to have some functions and I'm going to just do some copy and paste we're going to say add remove and list our inventory system we want to be able to add you guessed it some item with a quantity and remove some item with a quantity and then list basically we want to know what we have in our inventory from there I'm going to make a minor design decision that at this point in the series may not make a whole lot of sense I'm gonna say public slots so notice we already have signals and now we have slots so signals and slots we've touched on these but basically a signal lets the outside world know hey something happened some event happened it's emitting a signal a slot would consume that or take some sort of action this is event driven programming so cause and effect signal slot so when a signal is emitted you can connect to a slot and have that slot be called automatically from here I'm going to paste in save and load so the first question I'm often asked is what what what's the difference between a slot and a function virtually not to be brutally honest with you you can call a slot like a function but in order to use cute's built-in event-driven system called signals and slots you have to have a slot and this is where mock the meta object compiler comes in basically because we have this Q object macro when we compile it's going to see this and it's going to scanner class and go oh you have some slots and it's going to convert these functions into magical functions called slots which does all that event-driven programming in the background for us if it sounds confusing because it's actually pretty confusing under the hood but let's go ahead and flush this out and I'll show you what I mean here I'm also going to have a private variable called m underscore items which is going to be a Q map with a key value pair of Q string and int so there's our item name and our quantity from here we're just going to right click refractor and then add definition I'm gonna do this for each one of these they may be asking yourself why as I'm doing this why did we make those two slots but the others are just normal functions again it's just simply a design decision when you get a little bit more advanced you will literally take these and just copy and paste them under slots that way all of them are slots because you'll see the power of slots it's how you would get one Q object to automagically that's a word now automatically talk to another Q object by a signals and slots but you see in the implementation it's just normal functions so we can call save and load like normal functions or we have the options of using event driven programming signals and slots I'm gonna go ahead and save and I'm going to build we got a good build so we are ready to start fleshing out code but before we do let's jump out to the hard disk I want to talk about mock super quick we're gonna go to cute this is our build folder and here is our actual binary on windows you're gonna see like dot exe and you see we have some stuff here now let's look at this auto gen oh la la look at that mock meta object compiler so this is what's going on here is it's actually going out and generating this code and you see here is our mock underscore inventory our class was named inventory and our slots are save and load and if we crack this bad boy open you'll see save and load so that's what mocks doing in the background is it's taking any function that we define as a slot and it's generating this mock code for us so that as we make more complex applications we can connect using signals and slots I wanted to really cover that in this video even though we're not gonna use signals and slots very heavily because I'm gonna get those questions later on let's start filling in some code I'm gonna break this into two sections here so we're gonna do this code first and then in the next little section we're gonna do this code why because this is the inventory code this is really our internal business logic and this is our serialization code I want to really in your mind split those into two separate pieces anyways we can just switch to header and source and right let's go here so first thing I want to do is make a design choice again there's a billion different ways to really build this application but in the constructor I want this to automatically load our information which we're not gonna fill out just yet we're gonna load and this is going to load any file that we previously saved now notice we're calling this like a normal function that's what I mean by slots really are normal functions with mock meta object compile wrapped around them now from here we need to start flushing in some code so when we add an item you'll say if I'm underscore items my horrible typing included it no extra charge it's gonna say contains I've had this horrible I shouldn't say or is a great gaming keyboard when I bought it but I have to beat the hell out of this thing on different video games so I probably need to invest in a keyboard at some point and my mouse is the same way so once in a while you'll see me actually fight with the mouse I think it's kind of busted to be honest with you all right so what we're doing here is we're saying if our map M items contains the name or the key then we're gonna go ahead and say go ahead and take that item and add the quantity to it however if we don't already have that in our inventory we're gonna insert it we're gonna say m dot items dot insert and we're gonna insert a key value pair in this case our name and our quantity now somebody out there is going to say well if you call insert it will automatically do it for you the problem is it will overwrite the quantity instead of add to it so like if you go here F1 it says inserts a new item if there's already an item with the key that items value is replaced you gotta be a little careful there right now that we've got this we want to do the exact opposite which is removed so I'm gonna grab this copy paste and we're gonna say if it contains our key here then we want to do the opposite we want to reduce it but we've got a problem what if they go beyond zero so now I'm gonna say if m items dot value we want to get that value is less than zero and we're just gonna go ahead and remove it and this will actually take it right out of our map all together now I'm a big fan of letting the user know what's going on so I'm just going to say item removed now as you're watching me fumble around with this keyboard let me know if you prefer watching me type or if you prefer watching me do the old copy and paste like I'm doing there and I know some instructors actually have programs which will automatically type out all the code for you I don't use those I think they're a bit cheesy but again you wouldn't be watching me make a lot of mistakes so a quick recap we're adding and we are removing if we're removing an item and we go below zero we're just going to remove it right out of the map so we don't have negative numbers in our inventory alright to wrap this little section up we're gonna go ahead and list so I'm gonna say Q info items and we want to know how many items we have some say M items dot size there is a count which will return the same number but you can also count for specific items in there so we're just going to use size to avoid any confusion I'm gonna say for each I'm gonna say Q string key in M items dot keys and I'm pretty sure we covered this in our YouTube series I know I definitely beat this up in the Udemy courses but keys is just going to return a Q list of Q strings or whatever our key is remember we defined our Q map key value pair as a Q string for the key and a value as an int so we're just gonna say for each Q string key in the list of keys and then I just really want to print this out on the screen just so the user can see what we have an inventory it's actually pretty simple when you look at it in this terms so the major takeaway here is there's different ways of doing this we could have done the C++ way we could use for each we could have done a for loop using you know begin and end doesn't really matter as long as you get the result you want the end user simply doesn't care let's work on our serialization code so we've got two functions save and load save and load are pretty self-descriptive save is going to save to a file and load is going to load it back so let's save it first I'm gonna say Q file and let's call this file and we need a name inventory dot txt we can name this dot dat or whatever we wanted I'm just gonna name it dot txt so we can just crack it open very quickly in a notepad and see what it looks like I'm gonna say if and we're gonna go ahead and open this file file is not openable is that a word openable say Q IO device we want to open this in right only mode if we're not able to do this well we have some problems and we need to tell the user hey and no bueno couldn't do it so we're gonna say Q critical could not save the file and then we want to get the error string out of the Q file object that way we can tell the user hey something bad happened go deal with it and then we're just gonna hit the eject button on this and jump right out of here now comes the interesting bit this is something I don't think we've really covered a whole lot we're gonna say Q data stream and we're going to give it our file so what this does is it opens a file but now we are going to put or serialize some data into that file and we're gonna use Q data stream to stream the data and let it encode it and decode it for us but before we get to that point we need to tell it how so I'm gonna say stream set version and this is critical a lot of people and I'm guilty of it too we'll skip this step but we're going to explicitly say we want to work with cute version he knows it goes all the way back to 1.0 we want to set a version and say this is the version we're going to work with that way if somebody hands us a file that was encoded with an older version or a future version we know hey it's going to have a different encoding under the hood and we're going to check for that in the load section here but anyways what I want to do first is I want to write the number of items to the stream so I'm gonna say int Len equals M items and we want to get that size so I just want to know the number of items in that map and then writing it is extremely simple watch this that's it we're just going to pump it out to the stream and let the stream deal with all of the IO all the encoding it can just do what it does best and then I'm going to just give the user some sort of feedback that we're doing something number of items to save I'm gonna do a for each some people love for each some people hate it I've just gotten really used to doing it over the years but there are newer snazier ways of C++ to do it and we're gonna say M items that keys bang so really all we're gonna do is we're gonna go through each and every single little key here and we are going to save it so okay bang and then let's go ahead and say stream and we want to pump that key out the stream stream and now we want the values we're gonna just take the key value pair and put them in there but notice the order we're doing this in so we're putting the number of items then we're doing key value key value we're just gonna loop through that when we load this back the order in which we saved we have to read it in the same order we're gonna get corrupt data so be very mindful of that you'll see what I mean here in just a second so we're gonna say file close and then Q info I'm really envious of some of those instructors who will like have a program type it all out for me actually look into that I know I know I've asked that before and everybody's like no don't do that we like watching you type because we like watching you suffer anyways alright so I'm just gonna copy all of that code quick recap what we're doing here is we're opening a file called inventory.txt you can 100% put that somewhere else we're gonna say if we're unable to open the file in write only mode then we want to let the user know we're gonna make a data stream set the version and then we're gonna pump our data out and we're doing this in a specific order the number of items and then key value key value key value over and over until we're done and then we're gonna close the file note in the close it will also flush so any data that's sitting here pending in the buffer is going to get flushed down to the desk flush just means it's going to force it to write down to the desk now load loads a little interesting it does the exact opposite so I'm going to paste this in here we're gonna say if file open in read only mode failed then it could not open the file this is where I always make mistakes cuz I get it all screwed up let's back up here real quick I want to actually check to see if the file exists some say if not file that exists then we're going to and I'm gonna change this to a warning some people are gonna say what is the difference really between like a Q info key debug key warning these are levels of logging in case you kind of skip those we really really beat those up in the Udemy courses but you can actually build your own logging system and then set levels and categories and all this other cool stuff but we're just going to say file does not exist that way the end user knows hey we got no inventory there's nothing to work with here hit the eject button if we're unable to open it and read only mode hit the eject button now comes to the meat of it here in our save code we did a set version so I'm going to grab this version and we're gonna check for this we're gonna say if get the mouse out of there yeah their mouse alright so we're gonna say if and we want to know the stream version is not equal to the version we're using then we've got problems and we're going to simply say Q critical now in a real world application you would try to you know match the version then use the function that uses that specific version and so on and so on or say hey here's our data migration wizard that our company has built that will migrate your data to the newest version but instead we're gonna say wrong data stream version because there is one and only one in the land of our inventory app then we're going to just say file closed because at this point we have an open file and we're going to hit the eject button some of you out there are probably going to be really really good at reading the documentation we really don't even need this because as soon as the Q file goes out of scope it's going to close itself automatically I like to make sure that I'm closing it because I want to be in control alright from here we have an inventory system we need to clean that out I'm say M items clear and that's just going to unapologetically wipe anything we already have in memory out of there and then we can start loading it so we want to know the maximum and then we're going to start working with some stuff so I'm gonna say stream and we're going to read in that max you may be wondering wait a minute how do you know what to do well if you go back to our save code the first thing we did was we saved the number of items so we're simply just reading that back and then we're going to do the same for each key value pair so we are saying int max we're reading the number of items we're going to read back in and then we can say Q info number of items to load and then I'm going to just wipe this stuff out so we don't get that confused actually I'm gonna say loaded okay so now comes well you may be thinking this is the confusing bit but this couldn't be any simpler I'm just gonna say four and I equals zero I is less than our max I plus plus so we're just gonna increment our eye now we're gonna work with a key value pair so I'm gonna say we want a Q string key and it Qt y and we're gonna read those in so I'm gonna say stream E and then stream Qt y again you got to read these back in the order you saved them remember in our save code we saved the key then we save the quantity key quantity so of course down here we have to do key quantity key quantity now that we have those loaded up we're gonna say m items insert and we're going to just simply add our new key value pair from here files gonna close and we're gonna say we have fully loaded that out to the end user so they're aware of what's going on quick double check before we move on let's make sure we've fleshed out every single little function and remember our constructor is gonna call that load function we just fleshed out so as soon as the class is constructed it's gonna go out there and try to load that file if we previously saved it okay now comes the fun part we need to flesh out our main function this is actually the bulk of our application here so I'm going to just copy and paste some stuff this is our simple inventory application we've made a class we filled in the code now we need to monitor the user input and we're going to use the Q text stream the difference between Q text stream and Q data stream is that data stream is a non readable where text stream is human readable so this is what we're gonna use to get the end users commands and we're of course going to use our inventory class that we just built I'm going to judiciously copy and paste code to save some time we're gonna create an instance of our inventory class we're gonna tell the user because users are dumb these are the things you're allowed to do notice some of the commands like add remove you have multiple items add space item space item remove same thing but others are just one so we're gonna have to split those up just be aware of that so I'm gonna say whoops not qtex format qtex stream this is why I copy and paste things because I can't type and talk at the same time darn it all right so we're gonna say STD in and what this is going to do is it's going to wrap the standard input with a qtex stream so we can use that functionality and this is where we need to make a design decision I'm going to loop forever but this is really poor practice we're just simply not there in our little tutorials there's a better way of doing this that we're gonna cover when we get a little bit more advanced some say Q info and we are going to tell the user because users are dumb enter a command probably not nice for me to say users are dumb but you know what I mean you all have that one user we got to like walk them through every little thing now the big problem here is because this is gonna loop forever we've got to be able to know when to stop looping so let's go ahead and say Q string let's put this line and we're gonna say stream dot read line so we're gonna take our text stream and we're gonna read a single line now from here we need to take that line and break it up because some of these will have multiple parts and we're gonna use what's called split some say Q string list I'm say line split and we're gonna split on the space because that's the format we chose again design decision we could have split on a comma or whatever we wanted to do now that we've gotten this far what we're gonna do is we're going to say all right I want to know what the end user actually just entered some say Q string man equals list at and I want the first position dot to upper and we have a subtle bug in our program I don't know if you caught it but we're assuming that there's even an item in here so we should probably back up here and say if list size is less than or equal to zero then we're gonna go ahead and break out of here that way if something weird happened the entered some gibberish or our program freaked out remember these are cross platforms so they're not gonna behave exactly the same on every single platform we just want to make sure that we have some sort of list to work with our command is going to be the first item at the zero position to upper and from here we can do some very very cool things like if man equals quit and we can say either a dot quit you're wondering what a is it's our actual Q core application and you see we're entering an event loop here so we can either quit or we can say a dot exit and specify an exit code either way we're gonna break out of here spoiler this may actually misbehave on some systems and we get more advanced we'll talk about how to fix that but just beware it's going to behave a little bit differently on everybody's systems I've seen it behave mostly the way you expect but sometimes it does just do weird stuff but now it's really pretty basic actually I mean we can just save just a smidge of time and say okay we're going to save the command is list we're gonna list if it's load we're gonna love save we're gonna save now if you copy and paste you get this like weird formatting like I would I got you can just control a and then you can go to refractor and then I'm sorry not refractor auto indent selection or control I and it will fix it for you absolutely love that feature because I copy and paste a lot in my videos but you notice what we're not doing yet we're not adding or removing and these are the tricky ones because in our little protocol they have multiple parts add or move so rather than treat each one separately what we're going to do is check both homes say if and those add or man and this is where horrible spelling like I have will come back and haunt you and that's why in more advanced programs will actually use an enum instead of using a hard coded string again design decision based off of the knowledge we have so far if we're using these commands we know we need at least three things right we need a command a key and a value so I want to make sure we have those list size is less than we said we need three things then we're gonna tell the user hey do not pass go instead go directly to jail do not collect $200 that's a a board game in case you don't know what that is sex approved fun game alright so we're gonna say command add or remove if we don't have enough items we're just gonna say not enough info and we're going to continue meaning we're gonna restart this whole loop all over again and ask them for another man if we've gotten this far we're gonna go ahead and get that information some say Q string name list at we're gonna go ahead and get the first item now remember first is not first actually the second because it's a zero based index the zero position is actually our command the one position is our key now we're gonna go ahead and we're gonna say bull okay it's QTY the reason why we're gonna use a bull is we're going to convert from a string to an integer but we need to make sure it actually works the way we expect some say QTY equals list dot at and we're gonna get at the second position I'm gonna convert this to int and we're gonna give it our bull so we're gonna say if it doesn't work I want to know so if this is true it worked if it's false it didn't work and then we can just simply check that by doing something like this and again horrible formatting we're gonna say if it's not okay meaning if it's false in valid quantity so here we're doing some conversion I get asked that quite a bit how do you convert from int to string and then once we have this again it is ridiculously simple we're gonna say if the commands add inventory add if the commands remove inventory remove now when we get to this point sometimes we have to save the users from themselves so this is another design decision I'm gonna automatically save this once we break out of that loop so I'm gonna say inventory dot save because you always get that one user that goes well I know I saved it and no they did not so we're just gonna save it for them just to be safe some people love that feature some people will hate it a production app you would be able to turn this on or off all right let's go ahead and give it a good build before we move on make sure I didn't goof anything up all right we've got a good build we can move on to testing okay that was a lot of code if you're still with me it's time to test this application let's go ahead and run it all right so it says file does not exist because we haven't created one yet here's our available commands because well let's face it and users need to be told what to do and enter a command so first things first let's go ahead and add something so I'm gonna say add cat and we want to add 22 cats because you know life's just not worth living if you don't have 22 cats now enter a command let's go ahead and add and we're going to add three dogs actually love dogs just as much as I love cats but all right we're gonna go ahead and list so we have two items cat and dog let's go ahead and quit now notice it says number of items to save to saving cat saving dog file saved file saved so that auto save actually worked for us but notice we didn't type the save command let's go out here and you see sure enough here's our inventory file let's crack this open this is what I mean by it serialized data this is not human readable and our little text editor this is a I'm on Linux Mint so this is a Zed it's struggling to try to figure out what this is because it's expecting text and this is not text it's binary encoded serialized data let's go back here close this and run it notice it says number of items loaded to so our auto load is now working let's go ahead and list this we've got 22 cats and three dogs sweet all right so let's go ahead and remove let's remove two dogs oh notice I misspelled that so are not enough info actually helped us here so we're gonna go ahead and try that again remove dog to now it actually remove we're gonna go ahead and list it we have one dog left and then cats 22 cats is a lot of cat food so let's go ahead and remove some cats here some say remove let's remove 19 cats now we have three cats one dog and we're gonna test that to see if we can actually remove and get into a negative value so I'm going to remove dog we're gonna remove 99 dogs item removed now if we go ahead and list notice it took the dog out so we don't have a negative number instead we just have cat and we can if we wanted to go ahead and save this manually and if we wanted to we can also load it so there you go very simple inventory using cute no application is complete without bugs so we've got a pretty nasty bug in this application let's go ahead and fix it before we finish this video first I'm wondering if you know what the bug is so take a moment pause and think about it okay enough pausing it's quit so if we look at the quit command we have a exit let's go ahead and look at exit tells the application to exit with a return code after this function is called the application leaves the main event loop and returns from the call to exec wait what the function returns a return code if the event loop is not running the function does nothing so there's the bug right there we're telling it to quit but we haven't even entered into our event loop yet so when we do this and we type the word quit notice this cursor is still blinking so this app is actually still running we didn't really quit our application oh alright and to prove that we can go into projects and then uncheck run in terminal say run and you see we're now running inside of cute creator under the application tab and we can type the word quit and it's still running ta-da so we're gonna force us to stop that is a bug we need to fix this so there's a couple different ways we could do this I'm gonna go back to run in terminal so we can see it in real time first thing I'm gonna do we really don't need the event loop and we can play around with a that exit but it really doesn't do anything so let's try this really and let's say quit now notice our application does stop because it says press return to close this window so now this did actually quit but it didn't really work the way you expect it because if the if we're not in an event loop the function does nothing so basically does nothing we break and then it goes all the way down here right so instead what we're going to do is we're going to return zero assuming that everything worked good because that's really all a exec does is it takes our q-core application interest in event loop and then once it exit it generates an integer so we're just gonna return zero we don't even need this to be brutally honest so I'm just gonna comment that out and let's save and run try that again ta-da so it's working as expected now now I don't want to mislead you there are probably tons of little bucks in this application because we're talking about this from a very simple beginners level we're only like 20 some odd videos into this series so expect as we continue these videos they're going to get more and more and more elaborate and complex and we'll probably have more difficult bugs to be brutally honest but we'll go through this journey together and figure it out if you're interested the source code will be out on get up I've been really bad about that but I will 100% upload the code right after I upload this video I hope you enjoyed this video you can find the source code out on github.com if you need additional help myself and thousands of other developers are hanging out in the void realms Facebook group this is a large group with lots of developers and we talk about everything technology related not just the technology that you just watched and if you want official training I do develop courses out on you to me.com this is official classroom style training if you go out there and the course you're looking for is just simply not there drop me a note I'm either working on it or I will actually develop it I will put a link down below for all three of those and as always help me help you smash that like and subscribe button the more popular these videos become the more I'll create and publish out on YouTube thank you for watching