Nazalassa Posted March 11, 2023 Author Share Posted March 11, 2023 14 hours ago, HB Stratos said: I can't fully explain everything, but you can read up on the basics of what you can do with KAls on my kerbalX posts: https://kerbalx.com/HB_Stratos/Analog-Subtraction , https://kerbalx.com/HB_Stratos/Smoothed-State-Follower @Nazalassa by the way in my research of craft files I discovered something: Look at that movementTransformName... You can enter any transform there. So far I have gotten it to work with model, light_08 and spotlight. The first two make the entire spotlight move including it's base, while the last one only makes the light move without even moving the lamp (It's the spotlight you can pitch and yaw around). I am currently trying to find out which unity function they are using to parse that string, and then try to do path traverse so I can modify parts outside of the current part to move around. Would you have any ideas how any of this could work? MODULE { name = ModuleLight isEnabled = True isOn = True uiWriteLock = False disableColorPicker = False lightR = 0.875 lightG = 0.875 lightB = 0.875 castLight = True movementTransformName = Lamp isBlinking = False rotationAngle = 0 pitchAngle = -22 blinkRate = 1 stagingEnabled = True EVENTS { If we want it to be stock, then we shouldn't modify the configs :) But just studying them. 17 hours ago, HB Stratos said: I wonder whether it would be possible to hack this somehow so that e.g. the position of a sensor would affect the play position of a KAL. sounds near impossible... but maaaaybe.. Finding the name of the "[...]Incremental" thing would make it possible. So maybe we should get into KSP doc and try to understand how it works? 16 hours ago, Jacob Kerman said: So let me get this straight. You two are building a computer out of modules of KAL-1000 controllers. It can only sense one type of event. And it is soon to reach basic 8-bit computer standards. Please tell me how this works, as I have no idea about how this works. I can tell you how to fix a car, how to build a rocket, and how to wire a house, but I know very little about coding. That well... May be a bit too complex to explain it in one post. I will try to get a meaningful doc for it as soon as I can. [sorry] But if you look at the KerbalX pages, you'll fine some info. Not about what the purpose of a bus is, or the way a computer works, but it'll surely help understand how the KAL things work. Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted March 11, 2023 Author Share Posted March 11, 2023 (edited) On 3/10/2023 at 2:19 AM, HB Stratos said: I in the meantime have made progress on the parser. It still is very janky, in desperate need of a re-write. It will break on stuff like the panther engine where one engine with two same moduleEngines exist, but otherwise it appears to be working, if very ugly (unused code, unreadable code, outdated comments, etc) . btw I made a version that doesn't care about multiple things having the same name, but it's like... Well it's harder to use and still very WIP It uses lists as name/value pairs... (Tree mistakenly removed. Can't find it anywhere. My mistake. Sorry.) /!\ I just realized... That tree's wrong. That's not one PART object with three "sub-objects", that's three different PART objects... Same goes for the others. Actually no, small update from the future (january 2024): with the 0.3 version of CFP, that's exactly how things are stored. Edited March 3, 2024 by Nazalassa Tree accidentally deleted :( Quote Link to comment Share on other sites More sharing options...
HB Stratos Posted March 12, 2023 Share Posted March 12, 2023 On 3/11/2023 at 12:43 PM, Nazalassa said: If we want it to be stock, then we shouldn't modify the configs But just studying them These are craft files, not configs. By definition anything is stock that loads and functions in a completely unmodded game. And modifying craft files does load in any install of ksp just fine, so while this may be 'cheating' for a career playthrough, I see it as completely fair game to modify externally On 3/11/2023 at 12:43 PM, Nazalassa said: Finding the name of the "[...]Incremental" thing would make it possible. So maybe we should get into KSP doc and try to understand how it works? Having someone familiar with the game and how it works would be very helpful. Or even decompiled source code as it exists for minecraft. But that may not be possible here, I know how to mod parts, but not code sadly. On 3/11/2023 at 2:06 PM, Nazalassa said: btw I made a version that doesn't care about multiple things having the same name, but it's like... Well it's harder to use and still very WIP It uses lists as name/value pairs... Running it prints the nested lists: And here's how it stores them internally: Levels are nested list level. (these are NOT from the same craft file. The first one is the program's output, while the second one is an "artistic interpretation" of how an imaginary craft file would be stored.) /!\ I just realized... That tree's wrong. That's not one PART object with three "sub-objects", that's three different PART objects... Same goes for the others. Looks pretty good already! I tried to avoid lists, but kinda failed with that. How did you manage to make named lists? Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted March 13, 2023 Author Share Posted March 13, 2023 18 hours ago, HB Stratos said: These are craft files, not configs. By definition anything is stock that loads and functions in a completely unmodded game. And modifying craft files does load in any install of ksp just fine, so while this may be 'cheating' for a career playthrough, I see it as completely fair game to modify externally Having someone familiar with the game and how it works would be very helpful. Or even decompiled source code as it exists for minecraft. But that may not be possible here, I know how to mod parts, but not code sadly. Looks pretty good already! I tried to avoid lists, but kinda failed with that. How did you manage to make named lists? Just lists with two items, I see the first one as the name and the second as a value. Just a remainder that the bottom picture is wrong :( I did it with a dictionary prototype which ended up being rejected due to numerous issues. 18 hours ago, HB Stratos said: Having someone familiar with the game and how it works would be very helpful. Or even decompiled source code as it exists for minecraft. But that may not be possible here, I know how to mod parts, but not code sadly. Wasn't there some kind of... Documentation? At https://www.kerbalspaceprogram.com/ksp/api/index.html. Quote Link to comment Share on other sites More sharing options...
HB Stratos Posted March 14, 2023 Share Posted March 14, 2023 7 hours ago, Nazalassa said: Just lists with two items, I see the first one as the name and the second as a value. Good solution for the issue of there not being able to be two dictionaries of the same name. Just makes it a little harder to make code to traverse through it. With dicts you can just do craft["parts"]["probe_core"]["Resource Electric Charge"]["Amount"] = 200 or similar tricks. You'd need a helper function to do it this readably with lists Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted March 14, 2023 Author Share Posted March 14, 2023 15 hours ago, HB Stratos said: Good solution for the issue of there not being able to be two dictionaries of the same name. Just makes it a little harder to make code to traverse through it. With dicts you can just do craft["parts"]["probe_core"]["Resource Electric Charge"]["Amount"] = 200 or similar tricks. You'd need a helper function to do it this readably with lists Yeah... I suppose the way I used to fill the lists from the craft file can also work. Quote Link to comment Share on other sites More sharing options...
Gargamel Posted March 19, 2023 Share Posted March 19, 2023 As this isn’t about the stock game, it has been moved to mod discussions. Quote Link to comment Share on other sites More sharing options...
HB Stratos Posted March 21, 2023 Share Posted March 21, 2023 On 3/19/2023 at 7:13 PM, Gargamel said: As this isn’t about the stock game, it has been moved to mod discussions. We are literally trying to build a computer within the stock game. That is the purpose of this thread. That there is some code here stems from the fact that we do not fancy clicking 500 times for one KAL, only for KSP to not save it lol. Quote Link to comment Share on other sites More sharing options...
Gargamel Posted March 21, 2023 Share Posted March 21, 2023 1 hour ago, HB Stratos said: We are literally trying to build a computer within the stock game. That is the purpose of this thread. That there is some code here stems from the fact that we do not fancy clicking 500 times for one KAL, only for KSP to not save it lol. Oh crap. Yeah. I saw KAL and it registered as KOS. My bad guys. Moved back! Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted March 30, 2023 Author Share Posted March 30, 2023 (edited) I think it's time to get back to that .craft parser and try to, well, make it able to modify KAL curves. In theory, it may be used to create whole ships out of thin air (sorry, thin void) but I think it'll take quite a lot of time. So let's focus on KAL stuff for now. Maybe I'll get something working like, next week. Smol EDIT: I added a rough, ugly, temporary, spaghetti-coded, but working CLI .craft browser: Spoiler Another EDIT: I added instructions to set the name or the value of existing entries, as well as adding or removing entries. I still need to add a way to add the {...} parts (although they can be removed). Spoiler Yet another EDIT: If you want to see cfp in action, there's a 10-minute-long asciicast here (speed x3): https://asciinema.org/a/572851?speed=3 And, the .craft file I was using: ship = default version = 1.12.3 description = An awesome chip type = SPH size = 2,1.17161369,2.00000024 rot = 0,0,0,0 missionFlag = Squad/Agencies/KerbinWorldFirstRecordKeepingSociety PART { data = Hello data2 = 42 data3 = 0,0,0 DATA4 { } DATA5 { SUBDATA { subData1 = 1 subData2 = Eriya } greeting = Hi greeting = Hello } data6 = END } Also I have an idea, which may be quite... Let's say "cool". Edited May 6, 2023 by Nazalassa Added asciicast Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted March 30, 2023 Author Share Posted March 30, 2023 (edited) Log 23.7 So if we can edit KAL curves without any limits (modifying .craft files) then can we make them shorter / longer than the KAL's length? And, if yes, can we access the part that isn't in the KAL's length? Or what happens if we ask for a part of the curve that has well, no curve at this point, because the curve is too short? Well, experiment conducted, here are the results: If the beginning of the curve is missing (understand: if we only defined a curve between 0.5 and 1 for example) then the first point will be brought back to 0. If the end of the curve is missing (understand: if we only defined a curve between 0 and 0.5 for example) then the last point will be brought back to 1. If the curve is defined between 0 and 1 at least, but there are points outside of the range [0,1], these points do exist. If there's no point at positions 0 or 1, then nothing will change. I set a point at -1 and one at 1, the curve nicely interpolated between the two (even if only the part in the range [0,1] was displayed). However, even if there are points outside of [0,1], tthey can not be accessed. So yes, you can have a point at -1, but you'll never reach it, even if you set the KAL position with another KAL. KAL positions are always capped between 0 and 1 when applied, no matter what you do. In conclusion: KAL curves are never too short, they always fit (at least) the range [0,1]. Whatever part of a KAL curve that is outside of the range [0,1] does exist, although it can (and will) never be reached. (class dismissed) Edited May 6, 2023 by Nazalassa Moved log 23.7 here Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted March 31, 2023 Author Share Posted March 31, 2023 Anyone has an idea for RAM? Also, what architecture should we use, Von Neuman or Harvard? (I may have mispelled them) I think we're quite close - the only issue is well, RAM, I guess we can use the same thing as registers, but the KAL count will be quite high then... And remember - we can't store 2 bytes in a single KAL, they're too imprecise. OK, maybe two, but not four, and read/write will be a pain. Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted April 2, 2023 Author Share Posted April 2, 2023 (edited) I made some "concept art" to see what the graphical thing would look like: Spoiler Edited March 3, 2024 by Nazalassa Moved mockup to Codeberg Quote Link to comment Share on other sites More sharing options...
HB Stratos Posted April 12, 2023 Share Posted April 12, 2023 I don't have much time rn, but I'm glad to see your progress! Looks really good, absolutely solid work! On 3/31/2023 at 6:43 PM, Nazalassa said: Anyone has an idea for RAM? Also, what architecture should we use, Von Neuman or Harvard? (I may have mispelled them) I think we're quite close - the only issue is well, RAM, I guess we can use the same thing as registers, but the KAL count will be quite high then... And remember - we can't store 2 bytes in a single KAL, they're too imprecise. OK, maybe two, but not four, and read/write will be a pain. for a simple program registers and rom might be enough. Ram though, yeah your method might be the only one available so far. Optimizing the amount of KALs for R/W or perhaps multiplexing may be an idea. Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted April 13, 2023 Author Share Posted April 13, 2023 20 hours ago, HB Stratos said: I'm glad to see your progress! Nothing done in the past two weeks :/ real life sure takes some processor time. Hopefully I can do something during the next holidays, they're only 10 days away. 21 hours ago, HB Stratos said: Optimizing the amount of KALs for R/W or perhaps multiplexing may be an idea. I don't think removing W KALs will be easy, if possible at all, but R KALscan probably be reduced... IDK if it'll be easy, but it's surely doable. Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted April 22, 2023 Author Share Posted April 22, 2023 (edited) Log 23.8 A couple more ideas for RAM (and other things) went through my head today, so I'll write them before I forget them. FIRST IDEA For RAM, maybe we can have like a loop of 256 KALs, each one representing an address space (i.e first KAL is address 0x00, second KAL is 0x01, and so on), and on each phys-tick (25 times per second) each KAL would take the value of the preceding one, like with a tape. One specific KAL is linked to a R and a W KAL, like a register, so we can read/write data from/to the bus to/from it (it's the tape's head). At the beginning, it corresponds to address 0x00, but after 1 phys-tick it corresponds to address 0x01, and so on. A counter is incremented when the KAL change values, so it indicates the address which address the "head" corresponds. Of course the counter is reset to 0 each time it reaches its maximum. When reading/writing, we wait until the counter has the right address in it: after that, we can perform read/write on the "head" and it will read from/write to the address that is in the counter, so the one we want to modify. Pros: - Very KAL-efficient, as it requires (amount of RAM units) + 3 KALs - Quite simple - It can also be used as a hard disk! Cons: - Access time can vary, and be very long. '---> This means that excessive use of registers, a lot of them, will probably be needed, or that programs will be slow as hell... Or both. '---> In either case, memory access will likely need to be blocking, to avoid implementing very complex logic, which can negate the benefits of that type of RAM. SECOND IDEA I studied .craft files a bit, and this is a KAL's PART: Spoiler PART { part = controller1000_4294451462 partName = Part persistentId = 2563744445 pos = 0.663985729,10.0025492,-0.706833422 attPos = 0.699999988,-0.0166683197,-1.34075487 attPos0 = 0,0.0166683197,0.640754819 rot = 0,0.707106709,-0.707106829,0 attRot = 0,0,0,1 attRot0 = 0,0.707106709,-0.707106829,0 mir = 1,1,1 symMethod = Mirror autostrutMode = Off rigidAttachment = False istg = -1 resPri = 0 dstg = 0 sidx = -1 sqor = -1 sepI = -1 attm = 1 sameVesselCollision = False modCost = 0 modMass = 0 modSize = 0,0,0 srfN = srfAttach,structuralPanel2_4294458818,panel,0|0|0,0|0|-1,0|0|0 EVENTS { } ACTIONS { ToggleSameVesselInteraction { actionGroup = None wasActiveBeforePartWasAdjusted = False } SetSameVesselInteraction { actionGroup = None wasActiveBeforePartWasAdjusted = False } RemoveSameVesselInteraction { actionGroup = None wasActiveBeforePartWasAdjusted = False } } PARTDATA { } MODULE { name = ModuleRoboticController isEnabled = True persistentId = 2591903450 displayName = INPUT A5 sequencePosition = 10 sequencePlaySpeed = 0 sequenceLength = 10 controllerEnabled = True priorityField = 1 windowPosition = (246, -392) windowSize = (618, 419) stagingEnabled = True sequenceIsPlaying = False sequenceDirection = Forward sequenceLoopMode = Repeat EVENTS { } ACTIONS { TogglePlayAction { actionGroup = Custom10 wasActiveBeforePartWasAdjusted = False } // etc etc etc. } AXISGROUPS { sequencePosition { axisGroup = None axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 axisSpeedMultiplier = 0 axisInverted = None overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 } sequencePlaySpeed { axisGroup = None axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 axisSpeedMultiplier = 0 axisInverted = None overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 } } CONTROLLEDAXES { AXIS { persistentId = 210973617 moduleId = 4023821551 partNickName = Spotlight Mk1 rowIndex = 0 axisName = lightR timeValueCurve { key = 0 0 0 0 key = 0.498 0 0 0 key = 0.5 1 0 0 key = 1 1 0 0 } } AXIS { persistentId = 210973617 moduleId = 4023821551 partNickName = Spotlight Mk1 rowIndex = 1 axisName = lightG timeValueCurve { key = 0 0 0 0 key = 0.498 0 0 0 key = 0.5 1 0 0 key = 1 1 0 0 } } AXIS { persistentId = 210973617 moduleId = 4023821551 partNickName = Spotlight Mk1 rowIndex = 2 axisName = lightB timeValueCurve { key = 0 0 0 0 key = 0.498 0 0 0 key = 0.5 1 0 0 key = 1 1 0 0 } } AXIS { persistentId = 3915516560 moduleId = 3186489012 partNickName = KAL-1000 Controller rowIndex = 3 axisName = sequencePosition timeValueCurve { key = 0 0 0 0 key = 0.5 0 0 0 key = 0.51 1 0 0 key = 1 1 0 0 } } } CONTROLLEDACTIONS { } UPGRADESAPPLIED { } } MODULE { name = ModuleCargoPart isEnabled = True beingAttached = False beingSettled = False reinitResourcesOnStoreInVessel = False stagingEnabled = True EVENTS { } ACTIONS { } UPGRADESAPPLIED { } } } There are several very interesting (and possibly useful) things in this thing. In this section: Spoiler AXISGROUPS { sequencePosition { axisGroup = None axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 axisSpeedMultiplier = 0 axisInverted = None overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 } sequencePlaySpeed { axisGroup = None axisIncremental = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 axisSpeedMultiplier = 0 axisInverted = None overrideIncremental0 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental1 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental2 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 overrideIncremental3 = Pitch, Yaw, Roll, TranslateX, TranslateY, TranslateZ, WheelSteer, WheelThrottle, Custom01, Custom02, Custom03, Custom04 } } These are the ModuleRoboticController's "axis groups", the ones we can assign to KAL fields (KAL position ad KAL speed). We can see that the first one is named sequencePosition, and if you look in the big thing above, there is a field named sequencePosition in the module. Hypothesis: duplicating this entry and changing sequencePosition to priorityField (which I guess is the controller's priority) would allow us to link it to, for example, throttle, like we can do with sequencePosition. We can also see that there is a list of axisIncremental (which seems to be the same as the overrideIncremental[0-3]) which seems to be the "action fields" (throttle, pitch, etc.) of the Action Group panel (in the VAB or in flight). So maybe we can add any field of any part to the list of fields that can be linked to "action fields"? Cool. May be useful with Docking Port Kraken Drives, for example. We also have axisGroup, which (by its name) must be the "action field(s)" to which the field is linked. This requires experimentation. This does not seem to work for axis fields other than those that arelinkable to AGs by default. In this section: Spoiler CONTROLLEDAXES { AXIS { persistentId = 210973617 moduleId = 4023821551 partNickName = Spotlight Mk1 rowIndex = 0 axisName = lightR timeValueCurve { key = 0 0 0 0 key = 0.498 0 0 0 key = 0.5 1 0 0 key = 1 1 0 0 } } // Spotlight Green & Blue AXIS { persistentId = 3915516560 moduleId = 3186489012 partNickName = KAL-1000 Controller rowIndex = 3 axisName = sequencePosition timeValueCurve { key = 0 0 0 0 key = 0.5 0 0 0 key = 0.51 1 0 0 key = 1 1 0 0 } } } We have the timeValueCurve, which we already know (it's the list of pointsin the KAL's curve). [We'll be studying the first of the two, the one with persistentId = 210973617] But we also have moduleId, which value (4023821551) can be found again in the .craft here: Spoiler PART { part = spotLight3_4294162206 partName = Part persistentId = 210973617 pos = 0.663985729,10.1025496,-0.706833422 attPos = 0.400866687,0.0833320618,-0.967816114 attPos0 = 0.29913336,0.0166683197,0.267816067 rot = 0,0.707106709,-0.707106829,0 attRot = 0,0,0,1 attRot0 = 0,0.707106709,-0.707106829,0 // Stuff here MODULE { name = ModuleLight isEnabled = True persistentId = 4023821551 isOn = True uiWriteLock = False disableColorPicker = False lightR = 1 lightG = 1 lightB = 1 castLight = True movementTransformName = Lamp isBlinking = False rotationAngle = 0 pitchAngle = 0 blinkRate = 1 stagingEnabled = True // More stuff here } // Even more stuff here } It's the ModuleLight's persistentId. But wait! If we look at the AXIS again, we have a persistentId, which value (4023821551) is the spotlight's own persistentId! So, to sumarize, in an AXIS{...}: - persistentId is the target part's persistentId. - moduleId is the target module's persistentId. It also appears that axisName is the name of the axis, in the target module of the target part (in our case, lightR). I've looked a bit more in the .craft, and it's also correct for the KAL (the second AXIS{...}), as well as - bah, every other part I guess. Hypothesis: We can act on any field, with a KAL controller, on any part of the vessel. Which means that we're able, for example, to control [ INSERT FIELD NAME HERE ]. This requires experimentation. Sadly, this does not seem to work, as I tried to add a track for controller priority, which didn't show up and was probably deleted by the game during ship loading. More stuff incoming, I'll save my changes before Firefox decides to crash or something before I lose all this. Edited May 6, 2023 by Nazalassa Fixed indentation levels (duh) -> Moved log 23.8 here Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted April 24, 2023 Author Share Posted April 24, 2023 (edited) On 3/6/2023 at 2:37 AM, HB Stratos said: By the way, is your adder compatible with negative numbers with my method of just treating the middle of the play positon space as zero? Note from future self: with 256-analog computations (my analog adder, bus, etc.) we can write the numbers in binary from 00000000 to 11111111 (0 to 255) and then say that we represent them using two's complement. So we can represent negative numbers like this, for example KAL position of 192 represents -64. In this case it's fully compatible with my adder. btw, everything in the above post can be considered as "stock", as it should be loaded without problems by KSP. I don't know if editing the craft will break it, though. Requires experimetation. Edited April 24, 2023 by Nazalassa Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted May 4, 2023 Author Share Posted May 4, 2023 So I added a clipboard to my cli craft file parser... It just works. Hmmm, maybe I should work on the GUI... Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted May 4, 2023 Author Share Posted May 4, 2023 (edited) I made the part that handles the tk window and the tree (plus its useless-because-I'm-too-lazy-to-properly-do-the-bindings scrollbar), as well as the one that fills the tree with the data from the nested lists. Actually, I wasn't expecting it to work First Try! Spoiler Next: do the scrollbar binding and add input fields, tabs, etc. EDIT: Oh, look, a working scrollba!r (the other things are not working :P) Spoiler Now the file I/O is working! OK, mostly the input - not the output (sigh) Can you guess what craft this is? Spoiler Oh, btw - source code (221 lines) Spoiler #!/bin/python3 IN_FILE, OUT_FILE, SHIPS, PATH_SEPARATOR, BR_OPEN, BR_CLOSE, GEOMETRY, add_more_defs_here = 0, 1, 2, 3, 4, 5, 6, 7 import sys import tkinter as tk from tkinter import ttk spacing = ' \t' cd = [] data = { IN_FILE : '', OUT_FILE : '', SHIPS : [[]], PATH_SEPARATOR : ':', BR_OPEN : '{', BR_CLOSE : '}', GEOMETRY : '640x480' } def stripUseless(line): ret, i = '', 0 if '//' in line: line = line[:line.index('//')] # Remove comments, if any while i < len(line) and line[i] in spacing: i += 1 # Don't copy beginning spaces while i < len(line): ret += line[i] # Copy whatever's left i += 1 if ret != '': if ret[-1] == '\n': ret = ret[:-1] while ret[-1] in spacing: ret = ret[:-1] # Remove trailing spaces return ret def do_help(): print('Craft File Parser [CFP] by Nazalassa\n') do_usage() def do_usage(): print('Usage: ' + sys.argv[0] + ' [ -i|-f /input/file ] [ -o /output/file ] [ -p path-separator ] [ -h | --help ] [-v | --version ]\n\nOptions:\n -f --file Sets the input file to /input/file\n -i --input Sets the input file to /input/file\n -o --output Sets the output file to /output/file\n -p --path-separator Sets the character to use as a path separator\n\n -h --help Print this help and exit\n -v --version Print version info and exit\n\n/input/file and /output/file can be the same.\npath-separator is a single character, default is \':\'.') def do_version(): print('Craft File Parser [CFP] has no version :P') def parseArguments(): i = 0 while i < len(sys.argv): arg = sys.argv[i] if arg == '-h' or arg == '--help': do_help() sys.exit() elif arg == '-v' or arg == '--version': do_version() sys.exit() elif (arg == '-i' or arg == '-f' or arg == '--input' or arg == '--file') and (i < len(sys.argv) - 1): i += 1 data[IN_FILE] = sys.argv[i] elif (arg == '-o' or arg == '--output') and (i < len(sys.argv) - 1): i += 1 data[OUT_FILE] = sys.argv[i] elif (arg == '-p' or arg == '--path-separator') and (i < len(sys.argv) - 1): i += 1 data[PATH_SEPARATOR] = sys.argv[i][0] else: pass i += 1 if data[OUT_FILE] == '' and not data[IN_FILE] == '': data[OUT_FILE] = data[IN_FILE] if data[IN_FILE] == '' and not data[OUT_FILE] == '': data[IN_FILE] = data[OUT_FILE] def writeStruct(out, struct, ind): IND = '\t' * ind for n in struct: if type(n[1]) == list: out.write(f'{IND}{n[0]}\n{IND}{data[BR_OPEN]}\n') writeStruct(out, n[1], ind+1) out.write(f'{IND}{data[BR_CLOSE]}\n') else: out.write(f'{IND}{n[0]} = {n[1]}\n') def loadCraftFromFile(): in_file = open(data[IN_FILE], 'r') ID = len(data[SHIPS]) data[SHIPS] += [[]] cd = [data[SHIPS][ID]] line = in_file.readline() while line != '': line = stripUseless(line) # Get rid of spaces and tabs before data if '=' in line: # We're looking at a name-value couple equalPos = line.index('=') if equalPos == 0: sys.exit('Invalid .craft [ ' + line + ' ]') name, value = stripUseless(line[:equalPos]), stripUseless(line[equalPos+1:]) cd[-1] += [[name, value]] else: # We're probably looking at a sub-structure if line[0] == '}': # Exit sub-structure cd = cd[:-1] line = stripUseless(line[1:]) if line != '': if line[-1] == '{': # Enter sub-structure name, i = '', 0 while i < len(line) and line[i] not in spacing: name += line[i] i += 1 cd[-1] += [[name, []]] cd += [cd[-1][-1][1]] else: # We have to look at the next line nextLine = in_file.readline() if nextLine != '' and stripUseless(nextLine)[0] == '{': # Enter sub-structure name, i = '', 0 while i < len(line) and line[i] not in spacing: name += line[i] i += 1 cd[-1] += [[name, []]] cd += [cd[-1][-1][1]] #if nextLine == '': line = in_file.readline() line = in_file.readline() #else: line = nextLine in_file.close() return ID def saveCraftToFile(ID): out_file = open(data[OUT_FILE], 'w') cd = data[SHIPS][ID] writeStruct(out_file, cd, 0) out_file.close() def initTkWindow(root): root.geometry(data[GEOMETRY]) root.title('Craft File Parser - craft #0') root.rowconfigure(1, weight=1) root.columnconfigure(0, weight=1) def newFrame(root, mainColumn = []): nFrame = ttk.Frame(root) nFrame.rowconfigure(0, weight=1) for col in mainColumn: nFrame.columnconfigure(col, weight=1) return nFrame def loadDataToTree(tree, dat): tree.heading('#0', text='Name', anchor='w') tree.heading('#1', text='Value', anchor='w') for child in tree.get_children(): tree.delete(child) loadCraftFromFile() loadDataToTreeRecursive(tree, '', dat) def loadDataToTreeRecursive(tree, cd, dat): for item in dat: if type(item[1]) == list: loadDataToTreeRecursive(tree, tree.insert(cd, tk.END, text=item[0], values=[''], open=False), item[1]) else: tree.insert(cd, tk.END, text=item[0], values=[item[1]], open=False) def tkQuitPressed(): root.destroy() def tkSavePressed(): pass def tkLoadPressed(): data[IN_FILE] = strFileName.get() loadDataToTree(tree, data[SHIPS][-1]) def getObj(name, raw_cd): ret = [] for i in raw_cd: if i[0] == 0: ret += [i[1]] if len(ret) == 0: return None if len(ret) == 1: return ret[0] return ret def getShip(shipID = 0): return data[SHIPS][shipID] def main(): global root, tree, strFileName, strEntryName, strEntryValue parseArguments() if data[IN_FILE]: loadCraftFromFile() root = tk.Tk() tree = ttk.Treeview(root, columns='#1') treeScroll = ttk.Scrollbar(root) topFrame = newFrame(root, [3]) strFileName = tk.StringVar() fileQuit = ttk.Button(topFrame, text='Quit', command=tkQuitPressed) fileSave = ttk.Button(topFrame, text='Save', command=tkSavePressed) fileLoad = ttk.Button(topFrame, text='Load', command=tkLoadPressed) fileName = ttk.Entry(topFrame, textvariable=strFileName, font=('Unifont', 12)) fileName.focus() bottomFrame = newFrame(root, [0,1]) strEntryName = tk.StringVar() strEntryValue = tk.StringVar() entryName = ttk.Entry(bottomFrame, textvariable=strEntryName) entryValue = ttk.Entry(bottomFrame, textvariable=strEntryValue) tree.configure(yscrollcommand=treeScroll.set) treeScroll.configure(command=tree.yview) ttk.Style().configure('.', padding=1) initTkWindow(root) fileQuit.grid(row=0, column=0) fileSave.grid(row=0, column=1) fileLoad.grid(row=0, column=2) fileName.grid(row=0, column=3, sticky='nsew', padx=1, pady=1) topFrame.grid(row=0, column=0, columnspan=2, sticky='nsew') tree.grid(row=1, column=0, sticky='nsew') treeScroll.grid(row=1, column=1, sticky='ns') entryName.grid(row=0, column=0, sticky='nsew', padx=1, pady=1) entryValue.grid(row=0, column=1, sticky='nsew', padx=1, pady=1) bottomFrame.grid(row=2, column=0, columnspan=2, sticky='nsew') btn = ttk.Button(bottomFrame, text='Change name & value') btn.grid(row=0, column=2) loadDataToTree(tree, data[SHIPS][0]) root.mainloop() if __name__ == '__main__': main() If it complains that it cannot find the font 'Unifont', just remove the font=('Unifont', 12) part somewhere in main (line 188 iirc). Next, write a function to associate an item of the ttk.Treeview to its corresponding name/value pair. Edited March 3, 2024 by Nazalassa Moved mockups to Codeberg Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted May 4, 2023 Author Share Posted May 4, 2023 (edited) After spending thre hours rewriting about half the code, I got working tabs! You can add more and switch between them, and each one is independant (they can have different .craft open). I also added a but of code to show the first name/value pair in structures (like MODULEs or RESOURCEs) for better readability. Spoiler The source code has reached 291 lines (including the empty line at the bottom of the file). EDIT The save button now works, and pressing 'Quit' no longers closes the window - it closes the current tab (and the window if it was the last). Also, this button gives a warning if there are any unsaved changes ("Unsaved changes. Save them, or Quit again."), rather than forgetting them. The two bottom entries just don't work - I'll do them next. More EDITs So now you can modify the entries! (but not add/remove them) And you can save your work! And be warned when you try to quit without having saved Spoiler Source code so far: (369 lines) Spoiler #!/bin/python3 DATA, CFG, TK, NAME, IN_FILE, OUT_FILE, PATH_SEPARATOR, BR_OPEN, BR_CLOSE, GEOMETRY, ENTRY_FONT, UNSAVED_CHANGES, add_more_defs_here = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 tFrame, tree, treeScroll, topFrame, bQuit, bSave, bLoad, eFileName, bottomFrame, eEntryName, eEntryValue, bUpdateEntry, strFileName, strEntryName, strEntryValue = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 import os, sys, copy import tkinter as tk from tkinter import ttk spacing = ' \t' cd = [] data = { IN_FILE : '', OUT_FILE : '', PATH_SEPARATOR : ':', BR_OPEN : '{', BR_CLOSE : '}', GEOMETRY : '640x480', ENTRY_FONT : ('Unifont', 12), NAME : '', UNSAVED_CHANGES : 0 } TABS = [] currentTab = None def stripUseless(line): ret, i = '', 0 if '//' in line: line = line[:line.index('//')] # Remove comments, if any while i < len(line) and line[i] in spacing: i += 1 # Don't copy beginning spaces while i < len(line): ret += line[i] # Copy whatever's left i += 1 if ret != '': if ret[-1] == '\n': ret = ret[:-1] while len(ret) > 0 and ret[-1] in spacing: ret = ret[:-1] # Remove trailing spaces return ret def stripPath(path): if os.sep in path: path = path[path.rindex(os.sep)+1:] return path def do_help(): print('CFG File Parser [CFP] by Nazalassa\n') do_usage() def do_usage(): print('Usage: ' + sys.argv[0] + ' [ -i|-f /input/file ] [ -o /output/file ] [ -p path-separator ] [ -h | --help ] [-v | --version ]\n\nOptions:\n -f --file Sets the input file to /input/file\n -i --input Sets the input file to /input/file\n -o --output Sets the output file to /output/file\n -p --path-separator Sets the character to use as a path separator\n\n -h --help Print this help and exit\n -v --version Print version info and exit\n\n/input/file and /output/file can be the same.\npath-separator is a single character, default is \':\'.') def do_version(): print('CFG File Parser [CFP] has no version :P') def parseArguments(): i = 0 while i < len(sys.argv): arg = sys.argv[i] if arg == '-h' or arg == '--help': do_help() sys.exit() elif arg == '-v' or arg == '--version': do_version() sys.exit() elif (arg == '-i' or arg == '-f' or arg == '--input' or arg == '--file') and (i < len(sys.argv) - 1): i += 1 data[IN_FILE] = sys.argv[i] elif (arg == '-o' or arg == '--output') and (i < len(sys.argv) - 1): i += 1 data[OUT_FILE] = sys.argv[i] elif (arg == '-p' or arg == '--path-separator') and (i < len(sys.argv) - 1): i += 1 data[PATH_SEPARATOR] = sys.argv[i][0] else: pass i += 1 if data[OUT_FILE] == '' and not data[IN_FILE] == '': data[OUT_FILE] = data[IN_FILE] if data[IN_FILE] == '' and not data[OUT_FILE] == '': data[IN_FILE] = data[OUT_FILE] def loadCraftFromFile(tab): in_file = open(tab[DATA][IN_FILE], 'r') #ID = len(data[SHIPS]) #data[SHIPS] += [[]] cd = [tab[CFG]] #data[SHIPS][ID] line = in_file.readline() while line != '': line = stripUseless(line) # Get rid of spaces and tabs before data if line != '': if '=' in line: # We're looking at a name-value couple equalPos = line.index('=') if equalPos == 0: sys.exit('Invalid .craft [ ' + line + ' ]') name, value = stripUseless(line[:equalPos]), stripUseless(line[equalPos+1:]) cd[-1] += [[name, value]] else: # We're probably looking at a sub-structure if line[0] == '}': # Exit sub-structure cd = cd[:-1] line = stripUseless(line[1:]) if line != '': if line[-1] == '{': # Enter sub-structure name, i = '', 0 while i < len(line) and line[i] not in spacing: name += line[i] i += 1 cd[-1] += [[name, []]] cd += [cd[-1][-1][1]] else: # We have to look at the next line nextLine = in_file.readline() if nextLine != '' and stripUseless(nextLine)[0] == '{': # Enter sub-structure name, i = '', 0 while i < len(line) and line[i] not in spacing: name += line[i] i += 1 cd[-1] += [[name, []]] cd += [cd[-1][-1][1]] #if nextLine == '': line = in_file.readline() line = in_file.readline() #else: line = nextLine in_file.close() return tab[CFG] def saveCraftToFile(tab): out_file = open(tab[DATA][OUT_FILE], 'w') cd = tab[CFG] writeStruct(out_file, tab, cd, 0) out_file.close() def writeStruct(out, tab, struct, ind): IND = '\t' * ind for n in struct: if type(n[1]) == list: out.write(f'{IND}{n[0]}\n{IND}{tab[DATA][BR_OPEN]}\n') writeStruct(out, tab, n[1], ind+1) out.write(f'{IND}{tab[DATA][BR_CLOSE]}\n') else: out.write(f'{IND}{n[0]} = {n[1]}\n') def treeToTab(item): cd = [] while item != '': cd.insert(0, currentTab[TK][tree].index(item)) item = currentTab[TK][tree].parent(item) item = currentTab[CFG] for n in cd[:-1]: item = item[n][1] item = item[cd[-1]] return item def initTkWindow(root): root.geometry(data[GEOMETRY]) root.title('CFG File Parser') root.rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) def addTab(frame, tabs, in_file = '', out_file = ''): global TABS, currentTab TABS += [{DATA : copy.deepcopy(data), CFG : [], TK : {}}] if in_file: TABS[-1][DATA][IN_FILE] = in_file loadCraftFromFile(TABS[-1]) if out_file: TABS[-1][DATA][OUT_FILE] = out_file frame.rowconfigure(1, weight=1) frame.columnconfigure(0, weight=1) initTab(TABS[-1], frame, in_file) if in_file: loadDataToTree(TABS[-1]) TABS[-1][DATA][NAME] = stripPath(TABS[-1][DATA][IN_FILE]) else: TABS[-1][DATA][NAME] = 'new.craft' tabs.insert(tabs.index('end')-1, frame, sticky='nsew', text=TABS[-1][DATA][NAME]) tabs.select(tabs.index('end')-2) if TABS[-1][DATA][NAME]: root.title(f'CFG File Parser - {TABS[-1][DATA][NAME]}') else: root.title('CFG File Parser') currentTab = TABS[tabs.index('end')-2] tkTreeSelect(None) def initTab(tab, frame, fileName = ''): tab[TK] = {} tab[TK][tFrame] = frame tab[TK][strFileName] = tk.StringVar() tab[TK][strEntryName] = tk.StringVar() tab[TK][strEntryValue] = tk.StringVar() tab[TK][tree] = ttk.Treeview(tab[TK][tFrame], columns='#1') tab[TK][treeScroll] = ttk.Scrollbar(tab[TK][tFrame]) tab[TK][topFrame] = newFrame(tab[TK][tFrame], [3]) tab[TK][bQuit] = ttk.Button(tab[TK][topFrame], text='Quit', command=tkQuitPressed) tab[TK][bSave] = ttk.Button(tab[TK][topFrame], text='Save', command=tkSavePressed) tab[TK][bLoad] = ttk.Button(tab[TK][topFrame], text='Load', command=tkLoadPressed) tab[TK][eFileName] = ttk.Entry(tab[TK][topFrame], textvariable=tab[TK][strFileName], font=data[ENTRY_FONT]) tab[TK][bottomFrame] = newFrame(tab[TK][tFrame], [0,1]) tab[TK][eEntryName] = ttk.Entry(tab[TK][bottomFrame], textvariable=tab[TK][strEntryName]) tab[TK][eEntryValue] = ttk.Entry(tab[TK][bottomFrame], textvariable=tab[TK][strEntryValue]) tab[TK][bUpdateEntry] = ttk.Button(tab[TK][bottomFrame], text='Update entry', command=tkUpdateEntry) tab[TK][tree].configure(yscrollcommand=tab[TK][treeScroll].set) tab[TK][treeScroll].configure(command=tab[TK][tree].yview) tab[TK][tree].heading('#0', text='Name', anchor='w') tab[TK][tree].heading('#1', text='Value', anchor='w') tab[TK][tree].bind('<<TreeviewSelect>>', func=tkTreeSelect) tab[TK][strFileName].set(fileName) gridTab(tab) def gridTab(tab): tab[TK][bQuit].grid(row=0, column=0) tab[TK][bSave].grid(row=0, column=1) tab[TK][bLoad].grid(row=0, column=2) tab[TK][eFileName].grid(row=0, column=3, sticky='nsew', padx=1, pady=1) tab[TK][topFrame].grid(row=0, column=0, columnspan=2, sticky='nsew') tab[TK][tree].grid(row=1, column=0, sticky='nsew') tab[TK][treeScroll].grid(row=1, column=1, sticky='ns') tab[TK][eEntryName].grid(row=0, column=0, sticky='nsew', padx=1, pady=1) tab[TK][eEntryValue].grid(row=0, column=1, sticky='nsew', padx=1, pady=1) tab[TK][bUpdateEntry].grid(row=0, column=2) tab[TK][bottomFrame].grid(row=2, column=0, columnspan=2, sticky='nsew') def newFrame(root, mainColumns = []): nFrame = ttk.Frame(root) nFrame.rowconfigure(0, weight=1) for col in mainColumns: nFrame.columnconfigure(col, weight=1) return nFrame def loadDataToTree(tab): for child in tab[TK][tree].get_children(): tab[TK][tree].delete(child) loadDataToTreeRecursive(tab[TK][tree], '', tab[CFG]) def loadDataToTreeRecursive(tree, cd, dat): for item in dat: if type(item[1]) == list: val = '' if len(item[1]) > 0 and type(item[1][0][1]) != list: val = ' [ ' + item[1][0][0] + ' = ' + item[1][0][1] + ' ]' loadDataToTreeRecursive(tree, tree.insert(cd, tk.END, text=item[0], values=[val], open=False), item[1]) else: tree.insert(cd, 'end', text=item[0], values=[item[1]], open=False) def tkQuitPressed(): global prevFileName ctid = tabs.index(tabs.select()) if currentTab[DATA][UNSAVED_CHANGES]: if currentTab[DATA][UNSAVED_CHANGES] == 1: currentTab[DATA][UNSAVED_CHANGES] = 2 prevFileName = currentTab[TK][strFileName].get() currentTab[TK][strFileName].set('Unsaved changes. Save them, or Quit again.') return else: currentTab[TK][strFileName].set(prevFileName) if tabs.index('end') > 2: TABS.pop(ctid) if ctid == tabs.index('end')-2: tabs.select(ctid-1) tabs.forget(ctid) else: root.destroy() def tkSavePressed(): if currentTab[DATA][UNSAVED_CHANGES] == 2: currentTab[DATA][UNSAVED_CHANGES] = 0 currentTab[TK][strFileName].set(prevFileName) saveCraftToFile(currentTab) def tkLoadPressed(): currentTab[CFG] = [] currentTab[DATA][IN_FILE] = currentTab[TK][strFileName].get() currentTab[DATA][OUT_FILE] = currentTab[TK][strFileName].get() currentTab[DATA][NAME] = stripPath(currentTab[DATA][IN_FILE]) tabs.tab(currentTab[TK][tFrame], text=currentTab[DATA][NAME]) root.title(f'CFG File Parser - {currentTab[DATA][NAME]}') loadCraftFromFile(currentTab) loadDataToTree(currentTab) def tkUpdateEntry(): currentTab[DATA][UNSAVED_CHANGES] = 1 cl = currentTab[TK][tree].selection() if len(cl) == 1: cl2 = treeToTab(cl[0]) cl2[0] = currentTab[TK][strEntryName].get() val = '' if type(cl2[1]) != list: cl2[1] = currentTab[TK][strEntryValue].get() val = cl2[1] currentTab[TK][tree].item(cl[0], text=cl2[0], values=[val]) def tkTreeSelect(event): cl = currentTab[TK][tree].selection() if len(cl) == 0: currentTab[TK][strEntryName].set('NO ENTRY SELECTED') currentTab[TK][strEntryValue].set('NO ENTRY SELECTED') currentTab[TK][eEntryName].configure(state='disabled') currentTab[TK][eEntryValue].configure(state='disabled') currentTab[TK][bUpdateEntry].configure(state='disabled') elif len(cl) == 1: cl = treeToTab(cl) currentTab[TK][strEntryName].set(cl[0]) currentTab[TK][eEntryName].configure(state='normal') if type(cl[1]) == list: currentTab[TK][strEntryValue].set('STRUCTURE') currentTab[TK][eEntryValue].configure(state='disabled') else: currentTab[TK][strEntryValue].set(cl[1]) currentTab[TK][eEntryValue].configure(state='normal') currentTab[TK][bUpdateEntry].configure(state='normal') else: currentTab[TK][strEntryName].set('MULTIPLE ENTRIES SELECTED') currentTab[TK][strEntryValue].set('MULTIPLE ENTRIES SELECTED') currentTab[TK][eEntryName].configure(state='disabled') currentTab[TK][eEntryValue].configure(state='disabled') currentTab[TK][bUpdateEntry].configure(state='disabled') def tkTabChanged(event): global currentTab idx = tabs.index(tabs.select()) if currentTab[DATA][UNSAVED_CHANGES] == 2: currentTab[DATA][UNSAVED_CHANGES] = 1 currentTab[TK][strFileName].set(prevFileName) if idx == tabs.index('end')-1: addTab(ttk.Frame(tabs), tabs, in_file='', out_file='') else: currentTab = TABS[idx] root.title(f'CFG File Parser - {currentTab[DATA][NAME]}') #def getObj(name, raw_cd): #ret = [] #for i in raw_cd: #if i[0] == 0: #ret += [i[1]] #if len(ret) == 0: return None #if len(ret) == 1: return ret[0] #return ret #def getShip(shipID = 0): #return data[SHIPS][shipID] def main(): global root, tabs, currentTab parseArguments() #if data[IN_FILE]: loadCraftFromFile() root = tk.Tk() ttk.Style().configure('.', padding=1) initTkWindow(root) tabs = ttk.Notebook() addMoreTabs = ttk.Frame(tabs) tabs.add(addMoreTabs, sticky='nsew', text='+') addTab(ttk.Frame(tabs), tabs, in_file=data[IN_FILE], out_file=data[OUT_FILE]) tabs.grid(row=0, column=0, sticky='nsew') #loadDataToTree(tree, data[SHIPS][0]) tabs.bind("<<NotebookTabChanged>>", func=tkTabChanged) root.mainloop() if __name__ == '__main__': main() Edited March 3, 2024 by Nazalassa Moved mockups to Codeberg Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted May 5, 2023 Author Share Posted May 5, 2023 (edited) Now it is possible to delete one or more entries, and to insert the contents of a file at the end of the selected structure or, if the selection is not a structure, at the end of the parent structure of the selected item. Spoiler EDIT: I added the following: Tk theme from CLI (cfp-tk -t clam) geometry from CLI (cfp-tk -g 960x640) copy/pasting entry & structure insertion (which deleted my 256 analog adder, so I redownloaded it from KerbalX) Spoiler (later) Moving entries (with the '↕' button) (later) pressing 'return' in a text entry activates the corresponding button Spoiler I think I'm done with it. I'll probably add a 'scripts' button that open a window from which stuff can be run, but I don't really know. Source code size: 549 lines, 20.0 KiB I made a package with it! Which you can get from the git repo here. -- Also I have redone parts of the OP, such as the log and the PYTHON UTILITIES LIBRARY section. Edited March 3, 2024 by Nazalassa Moved mockups and package to Codeberg Quote Link to comment Share on other sites More sharing options...
HB Stratos Posted May 22, 2023 Share Posted May 22, 2023 That is absolutely awesome! Great job! I really have to look into writing scripts for your interface. something like a fairing smoother that just adds more sections for example. Or a single click add negative amount of ore to part to make it lighter (while ideally having precautions to avoid negative weight parts), A search would also be awesome, especially one that also allows ingame part names, not file names. So many ideas. Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted May 23, 2023 Author Share Posted May 23, 2023 (edited) On 5/23/2023 at 1:58 AM, HB Stratos said: That is absolutely awesome! Great job! I really have to look into writing scripts for your interface. something like a fairing smoother that just adds more sections for example. Or a single click add negative amount of ore to part to make it lighter (while ideally having precautions to avoid negative weight parts), A search would also be awesome, especially one that also allows ingame part names, not file names. So many ideas. I added a (collapsible) pane for scripts Spoiler which still needs to be functionnal (it isn't yet). For scripts, I'm thinking about a simple exec('import '+scriptName+'\n'+scriptName+'.init()') which, for `script`, will be the same as: import script script.init() If the script uses Tk, it will have to use a Toplevel() instance instead of a Tk() instance, and rely on Tk callbacks. Edited March 3, 2024 by Nazalassa Moved mockups to Codebergs Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted May 24, 2023 Author Share Posted May 24, 2023 (edited) @HB Stratos scripts support added! Now you have the "scripts pane" which displays all scripts found in the "scripts" folder. You can run them by pressing the "run" button. I have added an example script here. Spoiler Edited March 3, 2024 by Nazalassa Moved mockup and packages to Codeberg Quote Link to comment Share on other sites More sharing options...
Nazalassa Posted June 12, 2023 Author Share Posted June 12, 2023 (edited) Small update: this is what the KAL editing script currently looks. (I only got to it a few days ago due to procrastination.) Edited March 3, 2024 by Nazalassa Moved mockup to Codeberg Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.